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
|
||||
FROM python:3.10-slim AS GET_SCRIPTS_STAGE
|
||||
FROM python:3.10.6-slim AS GET_SCRIPTS_STAGE
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends git && \
|
||||
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_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready
|
||||
|
||||
@@ -22,22 +22,6 @@ services:
|
||||
aliases:
|
||||
- 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-dev:
|
||||
container_name: trmm-nats-dev
|
||||
|
||||
@@ -15,10 +15,7 @@ set -e
|
||||
: "${MESH_PASS:=meshcentralpass}"
|
||||
: "${MESH_HOST:=tactical-meshcentral}"
|
||||
: "${API_HOST:=tactical-backend}"
|
||||
: "${APP_HOST:=tactical-frontend}"
|
||||
: "${REDIS_HOST:=tactical-redis}"
|
||||
: "${HTTP_PROTOCOL:=http}"
|
||||
: "${APP_PORT:=8080}"
|
||||
: "${API_PORT:=8000}"
|
||||
|
||||
: "${CERT_PRIV_PATH:=${TACTICAL_DIR}/certs/privkey.pem}"
|
||||
@@ -142,16 +139,6 @@ if [ "$1" = 'tactical-init-dev' ]; then
|
||||
|
||||
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 -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${WORKSPACE_DIR}"
|
||||
chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${TACTICAL_DIR}"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
-r ../api/tacticalrmm/requirements.txt
|
||||
-r ../api/tacticalrmm/requirements-dev.txt
|
||||
-r ../api/tacticalrmm/requirements-test.txt
|
||||
-r /workspace/api/tacticalrmm/requirements.txt
|
||||
-r /workspace/api/tacticalrmm/requirements-dev.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
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10.4"]
|
||||
python-version: ["3.10.6"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -55,3 +55,4 @@ coverage.lcov
|
||||
daphne.sock.lock
|
||||
.pytest_cache
|
||||
coverage.xml
|
||||
setup_dev.yml
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
---
|
||||
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"
|
||||
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"
|
||||
scripts_dir: "/opt/trmm-community-scripts"
|
||||
trmm_dir: "{{ backend_dir }}/api/tacticalrmm/tacticalrmm"
|
||||
mesh_dir: "/opt/meshcentral"
|
||||
settings_file: "{{ trmm_dir }}/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:
|
||||
- build-essential
|
||||
@@ -22,7 +26,6 @@ base_pkgs:
|
||||
- g++
|
||||
- make
|
||||
- ca-certificates
|
||||
- redis
|
||||
- git
|
||||
|
||||
python_pkgs:
|
||||
|
||||
@@ -5,18 +5,24 @@ pid /run/nginx.pid;
|
||||
include /etc/nginx/modules-enabled/*.conf;
|
||||
|
||||
events {
|
||||
worker_connections 2048;
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
sendfile on;
|
||||
server_tokens off;
|
||||
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_protocols TLSv1.2 TLSv1.3;
|
||||
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;
|
||||
error_log /var/log/nginx/error.log;
|
||||
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"
|
||||
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
|
||||
tags: base
|
||||
become: yes
|
||||
@@ -19,6 +32,21 @@
|
||||
with_items:
|
||||
- "{{ 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
|
||||
tags: python
|
||||
become: yes
|
||||
@@ -63,31 +91,13 @@
|
||||
cmd: |
|
||||
make altinstall
|
||||
|
||||
- name: install nginx
|
||||
tags: nginx
|
||||
- name: install redis
|
||||
tags: redis
|
||||
become: yes
|
||||
ansible.builtin.apt:
|
||||
pkg: nginx
|
||||
pkg: redis
|
||||
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
|
||||
@@ -96,7 +106,7 @@
|
||||
dest: /etc/apt/sources.list.d/pgdg.list
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
mode: "0644"
|
||||
|
||||
- name: import postgres repo signing key
|
||||
tags: postgres
|
||||
@@ -232,6 +242,200 @@
|
||||
ansible.builtin.shell:
|
||||
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
|
||||
tags: django
|
||||
ansible.builtin.template:
|
||||
@@ -241,13 +445,189 @@
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
|
||||
- name: remove tempdirs
|
||||
tags: cleanup
|
||||
- name: setup django
|
||||
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
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
path: "{{ mesh_dir }}/meshcentral-data"
|
||||
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:
|
||||
- "{{ nats_tmp.path }}"
|
||||
- "{{ python_tmp.path }}"
|
||||
- "{{ nodejs_tmp.path }}"
|
||||
- nats
|
||||
- nats-api
|
||||
|
||||
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
|
||||
ALLOWED_HOSTS = ['{{ api }}']
|
||||
ADMIN_URL = "admin/"
|
||||
CORS_ORIGIN_WHITELIST = [
|
||||
"https://{{ rmm }}"
|
||||
]
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
@@ -17,3 +15,7 @@ DATABASES = {
|
||||
}
|
||||
REDIS_HOST = "localhost"
|
||||
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,
|
||||
UserUISerializer,
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
from accounts.utils import is_root_user
|
||||
|
||||
|
||||
class CheckCreds(KnoxLoginView):
|
||||
@@ -159,7 +148,7 @@ class GetUpdateDeleteUser(APIView):
|
||||
def put(self, request, 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")
|
||||
|
||||
serializer = UserSerializer(instance=user, data=request.data, partial=True)
|
||||
@@ -170,7 +159,7 @@ class GetUpdateDeleteUser(APIView):
|
||||
|
||||
def delete(self, request, 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")
|
||||
|
||||
user.delete()
|
||||
@@ -183,7 +172,7 @@ class UserActions(APIView):
|
||||
# reset password
|
||||
def post(self, request):
|
||||
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")
|
||||
|
||||
user.set_password(request.data["password"])
|
||||
@@ -194,7 +183,7 @@ class UserActions(APIView):
|
||||
# reset two factor token
|
||||
def put(self, request):
|
||||
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")
|
||||
|
||||
user.totp_key = ""
|
||||
|
||||
@@ -568,6 +568,12 @@ class Command(BaseCommand):
|
||||
check5_history.y = 1
|
||||
else:
|
||||
check5_history.y = 0
|
||||
check5_history.results = {
|
||||
"retcode": 0,
|
||||
"stdout": None,
|
||||
"stderr": None,
|
||||
"execution_time": "4.0000",
|
||||
}
|
||||
check5_history.save()
|
||||
|
||||
check6 = Check()
|
||||
@@ -595,6 +601,12 @@ class Command(BaseCommand):
|
||||
check6_history.agent_id = agent.agent_id
|
||||
check6_history.x = django_now - djangotime.timedelta(minutes=i * 2)
|
||||
check6_history.y = 0
|
||||
check6_history.results = {
|
||||
"retcode": 0,
|
||||
"stdout": None,
|
||||
"stderr": None,
|
||||
"execution_time": "4.0000",
|
||||
}
|
||||
check6_history.save()
|
||||
|
||||
nla_task = AutomatedTask()
|
||||
@@ -712,6 +724,12 @@ class Command(BaseCommand):
|
||||
check7_history.agent_id = agent.agent_id
|
||||
check7_history.x = django_now - djangotime.timedelta(minutes=i * 2)
|
||||
check7_history.y = 0
|
||||
check7_history.results = {
|
||||
"retcode": 0,
|
||||
"stdout": spooler_stdout,
|
||||
"stderr": None,
|
||||
"execution_time": "3.1337",
|
||||
}
|
||||
check7_history.save()
|
||||
|
||||
if agent.plat == AgentPlat.WINDOWS:
|
||||
|
||||
@@ -748,10 +748,10 @@ class Agent(BaseAuditModel):
|
||||
cache_key = f"agent_{self.agent_id}_checks"
|
||||
|
||||
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:
|
||||
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)
|
||||
if isinstance(cached_checks, list):
|
||||
@@ -773,10 +773,10 @@ class Agent(BaseAuditModel):
|
||||
cache_key = f"agent_{self.agent_id}_tasks"
|
||||
|
||||
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:
|
||||
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)
|
||||
if isinstance(cached_tasks, list):
|
||||
@@ -784,7 +784,7 @@ class Agent(BaseAuditModel):
|
||||
else:
|
||||
# get agent tasks based on policies
|
||||
tasks = Policy.get_policy_tasks(self)
|
||||
cache.set(f"site_{self.site_id}_tasks", tasks, 600)
|
||||
cache.set(cache_key, tasks, 600)
|
||||
return tasks
|
||||
|
||||
def _do_nats_debug(self, agent: "Agent", message: str) -> None:
|
||||
@@ -845,22 +845,22 @@ class Agent(BaseAuditModel):
|
||||
asyncio.run(
|
||||
send_command_with_mesh(cmd, mesh_uri, self.mesh_node_id, shell, 0)
|
||||
)
|
||||
return ("ok", False)
|
||||
return "ok", False
|
||||
|
||||
elif mode == "mesh":
|
||||
data = {"func": "recover", "payload": {"mode": mode}}
|
||||
if wait:
|
||||
r = asyncio.run(self.nats_cmd(data, timeout=20))
|
||||
if r == "ok":
|
||||
return ("ok", False)
|
||||
return "ok", False
|
||||
else:
|
||||
return (str(r), True)
|
||||
return str(r), True
|
||||
else:
|
||||
asyncio.run(self.nats_cmd(data, timeout=20, wait=False))
|
||||
|
||||
return ("ok", False)
|
||||
return "ok", False
|
||||
|
||||
return ("invalid", True)
|
||||
return "invalid", True
|
||||
|
||||
@staticmethod
|
||||
def serialize(agent: "Agent") -> Dict[str, Any]:
|
||||
|
||||
@@ -90,6 +90,11 @@ class AgentTableSerializer(serializers.ModelSerializer):
|
||||
last_seen = serializers.ReadOnlyField()
|
||||
pending_actions_count = 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):
|
||||
|
||||
@@ -141,16 +146,18 @@ class AgentTableSerializer(serializers.ModelSerializer):
|
||||
"plat",
|
||||
"goarch",
|
||||
"has_patches_pending",
|
||||
"version",
|
||||
"operating_system",
|
||||
"public_ip",
|
||||
"cpu_model",
|
||||
"graphics",
|
||||
"local_ips",
|
||||
"make_model",
|
||||
"physical_disks",
|
||||
]
|
||||
depth = 2
|
||||
|
||||
|
||||
class WinAgentSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Agent
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class AgentHostnameSerializer(serializers.ModelSerializer):
|
||||
client = serializers.ReadOnlyField(source="client.name")
|
||||
site = serializers.ReadOnlyField(source="site.name")
|
||||
|
||||
@@ -84,7 +84,7 @@ class TestAgentUpdate(TacticalTestCase):
|
||||
site=self.site1,
|
||||
monitoring_type=AgentMonType.SERVER,
|
||||
plat=AgentPlat.WINDOWS,
|
||||
version="2.3.0",
|
||||
version="2.1.1",
|
||||
)
|
||||
r = agent_noarch.do_update(token="", force=True)
|
||||
self.assertEqual(r, "noarch")
|
||||
@@ -106,7 +106,7 @@ class TestAgentUpdate(TacticalTestCase):
|
||||
site=self.site1,
|
||||
monitoring_type=AgentMonType.SERVER,
|
||||
plat=AgentPlat.WINDOWS,
|
||||
version="2.3.0",
|
||||
version="2.1.1",
|
||||
goarch=GoArch.AMD64,
|
||||
)
|
||||
|
||||
@@ -115,7 +115,7 @@ class TestAgentUpdate(TacticalTestCase):
|
||||
site=self.site3,
|
||||
monitoring_type=AgentMonType.WORKSTATION,
|
||||
plat=AgentPlat.LINUX,
|
||||
version="2.3.0",
|
||||
version="2.1.1",
|
||||
goarch=GoArch.ARM32,
|
||||
)
|
||||
|
||||
@@ -193,7 +193,7 @@ class TestAgentUpdate(TacticalTestCase):
|
||||
site=self.site2,
|
||||
monitoring_type=AgentMonType.SERVER,
|
||||
plat=AgentPlat.WINDOWS,
|
||||
version="2.3.0",
|
||||
version="2.1.1",
|
||||
goarch=GoArch.AMD64,
|
||||
_quantity=6,
|
||||
)
|
||||
@@ -215,7 +215,7 @@ class TestAgentUpdate(TacticalTestCase):
|
||||
site=self.site2,
|
||||
monitoring_type=AgentMonType.SERVER,
|
||||
plat=AgentPlat.WINDOWS,
|
||||
version="2.3.0",
|
||||
version="2.1.1",
|
||||
goarch=GoArch.AMD64,
|
||||
_quantity=7,
|
||||
)
|
||||
|
||||
@@ -422,13 +422,13 @@ class TestAgentViews(TacticalTestCase):
|
||||
url = f"{base_url}/{self.agent.agent_id}/reboot/"
|
||||
|
||||
# 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")
|
||||
self.assertEqual(r.status_code, 400)
|
||||
self.assertEqual(r.data, "Date cannot be set in the past")
|
||||
|
||||
# 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")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
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 meshctrl.utils import get_login_token
|
||||
from packaging import version as pyver
|
||||
from rest_framework import serializers
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
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 tacticalrmm.constants import (
|
||||
AGENT_DEFER,
|
||||
AGENT_TABLE_DEFER,
|
||||
AGENT_STATUS_OFFLINE,
|
||||
AGENT_STATUS_ONLINE,
|
||||
AgentHistoryType,
|
||||
@@ -114,7 +116,7 @@ class GetAgents(APIView):
|
||||
Agent.objects.filter_by_role(request.user) # type: ignore
|
||||
.filter(monitoring_type_filter)
|
||||
.filter(client_site_filter)
|
||||
.defer(*AGENT_DEFER)
|
||||
.defer(*AGENT_TABLE_DEFER)
|
||||
.select_related(
|
||||
"site__server_policy",
|
||||
"site__workstation_policy",
|
||||
@@ -166,6 +168,22 @@ class GetAgents(APIView):
|
||||
class GetUpdateDeleteAgent(APIView):
|
||||
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
|
||||
def get(self, request, 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):
|
||||
agent = get_object_or_404(Agent, agent_id=agent_id)
|
||||
|
||||
a_serializer = AgentSerializer(instance=agent, data=request.data, partial=True)
|
||||
a_serializer.is_valid(raise_exception=True)
|
||||
a_serializer.save()
|
||||
s = self.InputSerializer(instance=agent, data=request.data, partial=True)
|
||||
s.is_valid(raise_exception=True)
|
||||
s.save()
|
||||
|
||||
if "winupdatepolicy" in request.data.keys():
|
||||
policy = agent.winupdatepolicy.get() # type: ignore
|
||||
@@ -460,7 +478,7 @@ class Reboot(APIView):
|
||||
return notify_error(f"Not currently implemented for {agent.plat}")
|
||||
|
||||
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:
|
||||
return notify_error("Invalid date")
|
||||
|
||||
|
||||
@@ -7,8 +7,11 @@ from rest_framework.serializers import (
|
||||
from agents.serializers import AgentHostnameSerializer
|
||||
from autotasks.models import TaskResult
|
||||
from checks.models import CheckResult
|
||||
from clients.models import Client
|
||||
from clients.serializers import ClientMinimumSerializer, SiteMinimumSerializer
|
||||
from clients.models import Client, Site
|
||||
from clients.serializers import (
|
||||
ClientMinimumSerializer,
|
||||
SiteMinimumSerializer,
|
||||
)
|
||||
from winupdate.serializers import WinUpdatePolicySerializer
|
||||
|
||||
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):
|
||||
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:
|
||||
model = Client
|
||||
fields = ("pk", "name", "sites", "workstation_policy", "server_policy")
|
||||
depth = 2
|
||||
|
||||
|
||||
class PolicyCheckStatusSerializer(ModelSerializer):
|
||||
|
||||
@@ -2,8 +2,9 @@ from itertools import cycle
|
||||
from unittest.mock import patch
|
||||
|
||||
from model_bakery import baker, seq
|
||||
|
||||
from django.db.models import Prefetch
|
||||
from agents.models import Agent
|
||||
from clients.models import Site
|
||||
from core.utils import get_core_settings
|
||||
from tacticalrmm.constants import AgentMonType, TaskSyncStatus
|
||||
from tacticalrmm.test import TacticalTestCase
|
||||
@@ -186,7 +187,17 @@ class TestPolicyViews(TacticalTestCase):
|
||||
|
||||
baker.make("clients.Site", client=cycle(clients), _quantity=3)
|
||||
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)
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
@@ -7,10 +7,11 @@ from rest_framework.views import APIView
|
||||
from agents.models import Agent
|
||||
from autotasks.models import TaskResult
|
||||
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 winupdate.models import WinUpdatePolicy
|
||||
from winupdate.serializers import WinUpdatePolicySerializer
|
||||
from django.db.models import Prefetch
|
||||
|
||||
from .models import Policy
|
||||
from .permissions import AutomationPolicyPerms
|
||||
@@ -108,8 +109,18 @@ class PolicyCheck(APIView):
|
||||
class OverviewPolicy(APIView):
|
||||
def get(self, request):
|
||||
|
||||
clients = Client.objects.filter_by_role(request.user).select_related(
|
||||
"workstation_policy", "server_policy"
|
||||
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"
|
||||
),
|
||||
to_attr="filtered_sites",
|
||||
)
|
||||
)
|
||||
)
|
||||
return Response(PolicyOverviewSerializer(clients, many=True).data)
|
||||
|
||||
|
||||
@@ -70,11 +70,11 @@ class Client(BaseAuditModel):
|
||||
sites = self.sites.all()
|
||||
if old_client.workstation_policy != self.workstation_policy:
|
||||
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:
|
||||
for site in sites:
|
||||
cache.delete_many_pattern(f"site_server_{site.pk}_*")
|
||||
cache.delete_many_pattern(f"site_server_*{site.pk}_*")
|
||||
|
||||
class Meta:
|
||||
ordering = ("name",)
|
||||
@@ -145,10 +145,10 @@ class Site(BaseAuditModel):
|
||||
cache_agents_alert_template.delay()
|
||||
|
||||
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:
|
||||
cache.delete_many_pattern(f"site_server_{self.pk}_*")
|
||||
cache.delete_many_pattern(f"site_server_*{self.pk}_*")
|
||||
|
||||
class Meta:
|
||||
ordering = ("name",)
|
||||
|
||||
@@ -39,9 +39,8 @@ If (Get-Service $serviceName -ErrorAction SilentlyContinue) {
|
||||
$DefenderStatus = Get-MpComputerStatus | select AntivirusEnabled
|
||||
if ($DefenderStatus -match "True") {
|
||||
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:\Windows\Temp\trmm*\*'
|
||||
Add-MpPreference -ExclusionPath 'C:\ProgramData\TacticalRMM\*'
|
||||
}
|
||||
}
|
||||
Catch {
|
||||
|
||||
@@ -182,10 +182,10 @@ class CoreSettings(BaseAuditModel):
|
||||
test: bool = False,
|
||||
) -> tuple[str, bool]:
|
||||
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
|
||||
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
|
||||
if alert_template and alert_template.email_from:
|
||||
@@ -199,7 +199,7 @@ class CoreSettings(BaseAuditModel):
|
||||
elif self.email_alert_recipients:
|
||||
email_recipients = ", ".join(cast(List[str], self.email_alert_recipients))
|
||||
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:
|
||||
msg = EmailMessage()
|
||||
@@ -226,12 +226,12 @@ class CoreSettings(BaseAuditModel):
|
||||
except Exception as e:
|
||||
DebugLog.error(message=f"Sending email failed with error: {e}")
|
||||
if test:
|
||||
return (str(e), False)
|
||||
return str(e), False
|
||||
|
||||
if test:
|
||||
return ("Email test ok!", True)
|
||||
return "Email test ok!", True
|
||||
|
||||
return ("ok", True)
|
||||
return "ok", True
|
||||
|
||||
def send_sms(
|
||||
self,
|
||||
@@ -240,7 +240,7 @@ class CoreSettings(BaseAuditModel):
|
||||
test: bool = False,
|
||||
) -> tuple[str, bool]:
|
||||
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
|
||||
if alert_template and alert_template.text_recipients:
|
||||
@@ -248,7 +248,7 @@ class CoreSettings(BaseAuditModel):
|
||||
elif self.sms_alert_recipients:
|
||||
text_recipients = cast(List[str], self.sms_alert_recipients)
|
||||
else:
|
||||
return ("No sms recipients found", False)
|
||||
return "No sms recipients found", False
|
||||
|
||||
tw_client = TwClient(self.twilio_account_sid, self.twilio_auth_token)
|
||||
for num in text_recipients:
|
||||
@@ -257,12 +257,12 @@ class CoreSettings(BaseAuditModel):
|
||||
except TwilioRestException as e:
|
||||
DebugLog.error(message=f"SMS failed to send: {e}")
|
||||
if test:
|
||||
return (str(e), False)
|
||||
return str(e), False
|
||||
|
||||
if test:
|
||||
return ("SMS Test sent successfully!", True)
|
||||
return "SMS Test sent successfully!", True
|
||||
|
||||
return ("ok", True)
|
||||
return "ok", True
|
||||
|
||||
@staticmethod
|
||||
def serialize(core):
|
||||
|
||||
@@ -7,33 +7,33 @@ channels_redis==3.4.1
|
||||
chardet==4.0.0
|
||||
cryptography==37.0.4
|
||||
daphne==3.0.2
|
||||
Django==4.0.6
|
||||
Django==4.1
|
||||
django-cors-headers==3.13.0
|
||||
django-ipware==4.0.2
|
||||
django-rest-knox==4.2.0
|
||||
djangorestframework==3.13.1
|
||||
future==0.18.2
|
||||
msgpack==1.0.4
|
||||
nats-py==2.1.4
|
||||
nats-py==2.1.7
|
||||
psutil==5.9.1
|
||||
psycopg2-binary==2.9.3
|
||||
pycparser==2.21
|
||||
pycryptodome==3.15.0
|
||||
pyotp==2.6.0
|
||||
pyparsing==3.0.9
|
||||
pytz==2022.1
|
||||
pytz==2022.2.1
|
||||
qrcode==7.3.1
|
||||
redis==4.3.4
|
||||
hiredis==2.0.0
|
||||
requests==2.28.1
|
||||
six==1.16.0
|
||||
sqlparse==0.4.2
|
||||
twilio==7.12.0
|
||||
twilio==7.12.1
|
||||
urllib3==1.26.11
|
||||
uWSGI==2.0.20
|
||||
validators==0.20.0
|
||||
vine==5.0.0
|
||||
websockets==10.3
|
||||
zipp==3.8.1
|
||||
drf_spectacular==0.22.1
|
||||
drf-spectacular==0.23.1
|
||||
meshctrl==0.1.15
|
||||
|
||||
@@ -26,7 +26,7 @@ def process_nats_response(data: Union[str, Dict]) -> Tuple[bool, bool, str]:
|
||||
else "timeout"
|
||||
)
|
||||
|
||||
return (success, natserror, errormsg)
|
||||
return success, natserror, errormsg
|
||||
|
||||
|
||||
class GetServices(APIView):
|
||||
|
||||
@@ -61,4 +61,4 @@ class APIAuthentication(BaseAuthentication):
|
||||
if apikey.expiration and apikey.expiration < djangotime.now():
|
||||
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",
|
||||
)
|
||||
|
||||
AGENT_TABLE_DEFER = (
|
||||
"services",
|
||||
"created_by",
|
||||
"created_time",
|
||||
"modified_by",
|
||||
"modified_time",
|
||||
)
|
||||
|
||||
ONLINE_AGENTS = (
|
||||
"pk",
|
||||
"agent_id",
|
||||
|
||||
@@ -19,7 +19,7 @@ def get_certs() -> tuple[str, str]:
|
||||
cert_file = settings.CERT_FILE
|
||||
key_file = settings.KEY_FILE
|
||||
|
||||
return (cert_file, key_file)
|
||||
return cert_file, key_file
|
||||
|
||||
|
||||
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_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:
|
||||
|
||||
@@ -17,26 +17,26 @@ LINUX_AGENT_SCRIPT = BASE_DIR / "core" / "agent_linux.sh"
|
||||
AUTH_USER_MODEL = "accounts.User"
|
||||
|
||||
# latest release
|
||||
TRMM_VERSION = "0.14.5"
|
||||
TRMM_VERSION = "0.14.7"
|
||||
|
||||
# https://github.com/amidaware/tacticalrmm-web
|
||||
WEB_VERSION = "0.100.7"
|
||||
WEB_VERSION = "0.100.9"
|
||||
|
||||
# bump this version everytime vue code is changed
|
||||
# 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
|
||||
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"
|
||||
|
||||
# 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"
|
||||
|
||||
AGENT_BASE_URL = "https://agents.tacticalrmm.com"
|
||||
|
||||
@@ -35,8 +35,7 @@ nginxdefaultconf='/etc/nginx/nginx.conf'
|
||||
# increase default nginx worker connections
|
||||
/bin/bash -c "sed -i 's/worker_connections.*/worker_connections ${WORKER_CONNECTIONS};/g' $nginxdefaultconf"
|
||||
|
||||
sed -i '1s/^/worker_rlimit_nofile 1000000;\
|
||||
/' $nginxdefaultconf
|
||||
grep -q -e 'worker_rlimit_nofile' "${nginxdefaultconf}" || sed -i -e '/worker_processes.*/a\' -e 'worker_rlimit_nofile 1000000;' "${nginxdefaultconf}"
|
||||
|
||||
if [[ $DEV -eq 1 ]]; then
|
||||
API_NGINX="
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 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
|
||||
|
||||
@@ -21,14 +21,14 @@ RUN apt-get update && \
|
||||
pip install --no-cache-dir -r ${TACTICAL_TMP_DIR}/api/requirements.txt
|
||||
|
||||
# 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 && \
|
||||
apt-get install -y --no-install-recommends git && \
|
||||
git clone https://github.com/amidaware/community-scripts.git /community-scripts
|
||||
|
||||
# runtime image
|
||||
FROM python:3.10.4-slim
|
||||
FROM python:3.10.6-slim
|
||||
|
||||
# set env variables
|
||||
ENV VIRTUAL_ENV /opt/venv
|
||||
|
||||
4
go.mod
4
go.mod
@@ -13,11 +13,11 @@ require (
|
||||
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 (
|
||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // 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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
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/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
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/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
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-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
|
||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
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
|
||||
|
||||
SCRIPT_VERSION="66"
|
||||
SCRIPT_VERSION="67"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/install.sh'
|
||||
|
||||
sudo apt install -y curl wget dirmngr gnupg lsb-release
|
||||
@@ -12,7 +12,7 @@ RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
||||
PYTHON_VER='3.10.4'
|
||||
PYTHON_VER='3.10.6'
|
||||
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
||||
|
||||
TMP_FILE=$(mktemp -p "" "rmminstall_XXXXXXXXXX")
|
||||
|
||||
2
main.go
2
main.go
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "3.1.0"
|
||||
version = "3.2.0"
|
||||
log = logrus.New()
|
||||
)
|
||||
|
||||
|
||||
Binary file not shown.
@@ -74,24 +74,15 @@ func Svc(logger *logrus.Logger, cfg string) {
|
||||
stmt := `
|
||||
UPDATE agents_agent
|
||||
SET hostname=$1, operating_system=$2,
|
||||
plat=$3, total_ram=$4, boot_time=$5, needs_reboot=$6, logged_in_username=$7
|
||||
WHERE agents_agent.agent_id=$8;`
|
||||
plat=$3, total_ram=$4, boot_time=$5, needs_reboot=$6, logged_in_username=$7, goarch=$8
|
||||
WHERE agents_agent.agent_id=$9;`
|
||||
|
||||
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 {
|
||||
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" {
|
||||
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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_VERSION="40"
|
||||
SCRIPT_VERSION="41"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/restore.sh'
|
||||
|
||||
sudo apt update
|
||||
@@ -13,7 +13,7 @@ RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
||||
PYTHON_VER='3.10.4'
|
||||
PYTHON_VER='3.10.6'
|
||||
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
||||
|
||||
TMP_FILE=$(mktemp -p "" "rmmrestore_XXXXXXXXXX")
|
||||
@@ -175,7 +175,7 @@ print_green 'Restoring systemd services'
|
||||
sudo cp $tmp_dir/systemd/* /etc/systemd/system/
|
||||
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
|
||||
numprocs=$(nproc)
|
||||
|
||||
@@ -309,9 +309,9 @@ fi
|
||||
printf >&2 "\n\n"
|
||||
|
||||
#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
|
||||
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
|
||||
printf >&2 "\n\n"
|
||||
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
|
||||
|
||||
tail /rmm/api/tacticalrmm/tacticalrmm/private/log/django_debug.log | tee -a checklog.log
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_VERSION="138"
|
||||
SCRIPT_VERSION="139"
|
||||
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'
|
||||
@@ -10,7 +10,7 @@ NC='\033[0m'
|
||||
THIS_SCRIPT=$(readlink -f "$0")
|
||||
|
||||
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
||||
PYTHON_VER='3.10.4'
|
||||
PYTHON_VER='3.10.6'
|
||||
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
||||
|
||||
TMP_FILE=$(mktemp -p "" "rmmupdate_XXXXXXXXXX")
|
||||
|
||||
Reference in New Issue
Block a user