Compare commits

..

186 Commits

Author SHA1 Message Date
wh1te909
d1c3fc8493 Release 0.6.1 2021-04-16 07:46:42 +00:00
wh1te909
f453b16010 bump versions 2021-04-16 07:36:27 +00:00
wh1te909
05151d8978 add code signed agent to powershell/manual install methods 2021-04-16 07:16:55 +00:00
Dan
8218e1acc3 Merge pull request #397 from silversword411/develop
script library updates
2021-04-16 00:11:57 -07:00
wh1te909
30212fc89a fix maint mode text 2021-04-16 06:24:27 +00:00
sadnub
b31c13fcae add warning color to agents table and clients tree. Also made it upadte colors when checks UI is refreshed 2021-04-15 22:24:44 -04:00
sadnub
6b95fc6f1d change maintenance mode to green and modify the icon in the agent table when agent is in maintenance mode 2021-04-15 19:15:02 -04:00
sadnub
369cf17eb2 also resolve alerts when a check is cleared 2021-04-15 17:23:43 -04:00
sadnub
4dd8f512cc split up check statuses in the agent summary tab. #386 2021-04-15 17:12:46 -04:00
sadnub
26cfec7d80 add reset check status to check context menu. #388 2021-04-15 16:52:42 -04:00
sadnub
67a87ccf00 fix sticky table header in automated tasks 2021-04-15 16:12:09 -04:00
sadnub
667cebcf94 remove certain fields from view in the patch policy form when settnigs are inherited #396 2021-04-15 13:52:24 -04:00
sadnub
bc1747ca1c fix categories in script manager folder view. Truncate script args text in scripts table 2021-04-15 13:52:24 -04:00
silversword411
945d8647bf script add ipv6 disable 2021-04-15 11:34:35 -04:00
silversword411
dfe2e94627 tweaking script library after 0.6.0 update 2021-04-15 08:47:04 -04:00
silversword411
09a5591eec tweak docs so backup script overwrites existing name 2021-04-15 07:59:22 -04:00
silversword411
f2bf06a0ba tweak Network script names for sorting 2021-04-15 07:51:07 -04:00
silversword411
eedad4ab1c Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-15 07:47:41 -04:00
silversword411
336a62ab29 Tweaking script names 2021-04-15 07:47:31 -04:00
wh1te909
b5603a5233 Release 0.6.0 2021-04-15 05:39:24 +00:00
wh1te909
73890f553c bump versions 2021-04-15 05:34:47 +00:00
sadnub
f6243b8968 update community script to include guid and update/delete existing community scripts 2021-04-14 22:43:12 -04:00
sadnub
3770dc74d4 fix scripts dropdown 2021-04-14 21:48:39 -04:00
wh1te909
45f4e947c5 more code signing stuff 2021-04-15 01:24:58 +00:00
Dan
9928d7c6e1 Merge pull request #394 from tremor021/develop
Fixed script naming scheme
2021-04-14 18:20:31 -07:00
silversword411
bf776eeb2b Tweaking script names 2021-04-14 15:12:02 -04:00
tremor021
ae7c0e9195 update 2021-04-14 15:53:54 +02:00
tremor021
e90b640602 Merge branch 'develop' of https://github.com/wh1te909/tacticalrmm into develop 2021-04-14 15:48:37 +02:00
tremor021
ba7529d3f5 update 2021-04-14 15:30:57 +02:00
tremor021
34667f252e Fixed naming and added task scheduling script 2021-04-14 15:23:22 +02:00
wh1te909
d18bddcb7b add code signing auth token 2021-04-14 07:49:28 +00:00
wh1te909
96dff49d33 add better exception messages to tests 2021-04-14 07:39:17 +00:00
Dan
b389728338 Merge pull request #392 from tremor021/develop
Scripts update
2021-04-14 00:35:20 -07:00
tremor021
cdc7da86f3 Fixes 2021-04-14 09:26:42 +02:00
tremor021
4745cc0378 Fixes 2021-04-14 09:25:26 +02:00
wh1te909
434f132479 add a test to make sure community script has jsonfile entry 2021-04-14 07:05:08 +00:00
tremor021
fb0f31ffc7 Added script to json 2021-04-14 08:55:17 +02:00
Dan
bb1d73c0ae Merge pull request #393 from silversword411/develop
Script library additions
2021-04-13 23:32:33 -07:00
silversword411
0e823d1191 Fixing bitlocker get recovery keys script 2021-04-14 00:40:26 -04:00
silversword411
48f4199ff3 script library - bitlocker get recovery keys 2021-04-14 00:39:20 -04:00
silversword411
eaf379587b script library - disable hibernation 2021-04-14 00:04:43 -04:00
silversword411
672446b7d1 script library check if user using temp profile 2021-04-13 23:52:04 -04:00
silversword411
dfe52c1b07 script library - task scheduler monitor 2021-04-13 23:48:15 -04:00
silversword411
d63df03ad8 script library - new user notify 2021-04-13 23:42:34 -04:00
silversword411
aba4f9f2ce script library - Azure Mars Backup check 2021-04-13 23:37:05 -04:00
silversword411
ac5c1e7803 Script library - Adding enable and disable USB devices 2021-04-13 23:26:48 -04:00
tremor021
d521dbf50e Added more scripts 2021-04-13 23:39:44 +02:00
tremor021
f210ed3e6a Added Get_Computer_Users script 2021-04-13 23:25:10 +02:00
tremor021
df3cac4ea6 Merge branch 'develop' of https://github.com/wh1te909/tacticalrmm into develop 2021-04-13 22:14:14 +02:00
tremor021
f778c5175b Added some explanations into scripts 2021-04-13 22:10:37 +02:00
Dan
6c66ff28dd Merge pull request #385 from silversword411/develop
script library updates and docs tweak
2021-04-11 22:39:12 -07:00
silversword411
d5b6ec702b Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-11 11:36:20 -04:00
silversword411
c62a5fcef2 script library empty recycle bin2 2021-04-11 11:35:53 -04:00
silversword411
59c47e9200 script library - empty recycle bin 2021-04-11 11:35:53 -04:00
silversword411
4ba44d8932 script library - Monitor info script 2021-04-11 11:35:53 -04:00
silversword411
27dae05e1b Script add - RAM status 2021-04-11 11:35:53 -04:00
silversword411
a251ae9b90 2 scripts added 2021-04-11 11:35:53 -04:00
silversword411
7e960b2bde Fixing Table of Contents levels 2021-04-11 11:35:53 -04:00
silversword411
5df4825158 Consistency check on script "name" field for alphabetic sorts 2021-04-11 11:35:53 -04:00
silversword411
8984d06d93 script library empty recycle bin2 2021-04-11 11:26:59 -04:00
silversword411
eed7aac047 script library - empty recycle bin 2021-04-11 11:26:22 -04:00
silversword411
54b068de4a script library - Monitor info script 2021-04-11 11:21:03 -04:00
silversword411
f0f33b00b6 Script add - RAM status 2021-04-11 09:13:47 -04:00
silversword411
1043405088 2 scripts added 2021-04-11 08:47:36 -04:00
silversword411
0131b10805 Fixing Table of Contents levels 2021-04-11 08:02:51 -04:00
silversword411
a19b441f62 Consistency check on script "name" field for alphabetic sorts 2021-04-11 07:58:03 -04:00
wh1te909
28edc31d43 Release 0.5.3 2021-04-11 08:08:58 +00:00
wh1te909
0f9872a818 bump versions 2021-04-11 08:08:48 +00:00
wh1te909
76ce4296f3 fix graphics 2021-04-11 07:25:37 +00:00
wh1te909
3dd2671380 add graphics 2021-04-11 06:50:16 +00:00
wh1te909
298ca31332 remove unused func 2021-04-11 05:43:17 +00:00
wh1te909
8f911aa6b9 more tests 2021-04-11 05:35:24 +00:00
wh1te909
82a5c7d9b1 add test 2021-04-11 05:17:49 +00:00
wh1te909
7f013dcdba refactor nats-api / optimize queries 2021-04-11 05:04:33 +00:00
wh1te909
68e2e16076 add feat #377 2021-04-11 03:23:40 +00:00
wh1te909
ea23c763c9 add feat #376 2021-04-11 02:01:40 +00:00
wh1te909
5dcecb3206 fix alert text for policy diskspace check where disk doesn't exist 2021-04-10 22:09:24 +00:00
Dan
5bd48e2d0e Merge pull request #380 from silversword411/develop
Community Script Additions
2021-04-10 13:36:26 -07:00
silversword411
afd0a02589 3 scripts added from dinger1986 2021-04-10 13:44:02 -04:00
silversword411
2379192d53 Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-10 13:27:35 -04:00
silversword411
a6489290c8 2 scripts added 2021-04-10 13:26:39 -04:00
silversword411
5f74c43415 2 scripts added 2021-04-10 13:22:54 -04:00
wh1te909
aa8b84a302 Release 0.5.2 2021-04-09 18:30:30 +00:00
wh1te909
b987d041b0 bump version 2021-04-09 18:29:08 +00:00
wh1te909
b62e37307e revert meshcentral back to 0.7.93 2021-04-09 18:28:43 +00:00
Dan
61a59aa6ac Merge pull request #375 from silversword411/develop
scripts 4 adds and a rename
2021-04-09 00:09:17 -07:00
silversword411
f79ec27f1d Adding 5 new scripts 2021-04-09 01:05:58 -04:00
silversword411
b993fe380f Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-09 00:40:24 -04:00
silversword411
d974b5f55f Script screenconnect rename 2021-04-09 00:38:22 -04:00
wh1te909
f21ae93197 Release 0.5.1 2021-04-08 08:05:08 +00:00
wh1te909
342ff18be8 bump versions 2021-04-08 07:58:04 +00:00
wh1te909
a8236f69bf catch msgpack decode errors 2021-04-08 06:48:43 +00:00
wh1te909
ab15a2448d update reqs 2021-04-08 06:09:48 +00:00
wh1te909
6ff4d8f558 run migrations during restore 2021-04-08 05:57:16 +00:00
sadnub
bb04ba528c make sure logs dir exists for api 2021-04-07 19:39:35 -04:00
sadnub
b94a795189 specify names for the dev and prod containers and fix frontend web .env generation 2021-04-07 19:39:35 -04:00
wh1te909
9968184733 fix alert sending the wrong winsvc status text 2021-04-07 20:34:29 +00:00
silversword411
1be6f8f87a Script screenconnect rename 2021-04-07 11:56:02 +00:00
wh1te909
426821cceb django 3.2 2021-04-07 04:58:35 +00:00
wh1te909
4fec0deaf7 add another server for exe gen 2021-04-07 04:52:16 +00:00
Dan
144ac5b6ce Merge pull request #373 from silversword411/develop
Script rename-ageddon v1
2021-04-06 21:41:22 -07:00
Dan
97c73786fa Merge pull request #372 from bradhawkins85/patch-12
Update installer.ps1
2021-04-06 21:40:24 -07:00
silversword411
82e59d7da0 Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-07 04:09:24 +00:00
silversword411
b2c10de6af Script rename-ageddon v1 2021-04-07 04:09:04 +00:00
silversword411
d72029c2c6 Script rename-ageddon v1 2021-04-07 04:08:30 +00:00
bradhawkins85
17b9987063 Update installer.ps1
Set TLS version to 1.2
2021-04-07 13:47:26 +10:00
Dan
fde07da2b7 Merge pull request #371 from silversword411/develop
Script Category-geddon v1
2021-04-06 16:32:17 -07:00
silversword411
c23bc29511 Don't tell anyone, secret devbox rockin docs WIP 2021-04-06 04:12:56 +00:00
silversword411
714cad2a52 Script Category-geddon v1 2021-04-06 03:30:59 +00:00
wh1te909
357d5d2fde sort scripts alphabetically 2021-04-05 09:00:32 +00:00
Dan
d477cce901 Merge pull request #369 from bradhawkins85/patch-11
Update ScreenConnectAIO.ps1
2021-04-05 01:11:38 -07:00
bradhawkins85
eb6af52ad1 Update ScreenConnectAIO.ps1
Add error checking to ensure required custom fields have been created, stop the script if they have not been set up.
2021-04-05 17:51:01 +10:00
wh1te909
aae75023a7 add some more tests for community scripts json file 2021-04-05 07:21:47 +00:00
wh1te909
41dcd4f458 fix screenconnect args 2021-04-05 07:21:26 +00:00
Dan
4651ae4495 Merge pull request #368 from bradhawkins85/patch-10
Create ScreenConnectAIO.ps1
2021-04-04 23:45:50 -07:00
bradhawkins85
ed61e0b0fc Create ScreenConnectAIO.ps1
Install, Uninstall, Start and Stop ScreenConnect Access Agent
2021-04-05 16:42:05 +10:00
Dan
1eefc6fbf4 Merge pull request #367 from wh1te909/revert-365-patch-8
Revert "Create ScreenConnectAIO.ps1"
2021-04-04 23:40:33 -07:00
Dan
09ebf2cea2 Revert "Create ScreenConnectAIO.ps1" 2021-04-04 23:40:17 -07:00
Dan
b3b0c4cd65 Merge pull request #366 from bradhawkins85/patch-9
Update community_scripts.json
2021-04-04 23:21:52 -07:00
Dan
f4b7924e8f Merge pull request #365 from bradhawkins85/patch-8
Create ScreenConnectAIO.ps1
2021-04-04 23:21:44 -07:00
bradhawkins85
ea68d38b82 Update community_scripts.json
add ScreenConnectAIO.ps1
2021-04-05 16:18:29 +10:00
bradhawkins85
dfbaa71132 Create ScreenConnectAIO.ps1
Install, Uninstall, Start and Stop ScreenConnect Access agent script.
2021-04-05 15:59:08 +10:00
Dan
6c328deb08 Merge pull request #364 from silversword411/develop
Polishing vscode contribute docs
2021-04-04 22:29:50 -07:00
silversword411
add564d5bf Polishing vscode docs v2 2021-04-05 00:11:56 +00:00
silversword411
fa94acb426 Updating Disk Health and Duplicati scripts 2021-04-05 00:07:24 +00:00
silversword411
6827468f13 Polishing vscode contribute docs 2021-04-04 23:51:39 +00:00
Dan
53fd43868f Merge pull request #362 from silversword411/develop
Adding vscode contributing Howto to docs
2021-04-04 13:45:27 -07:00
silversword411
9ced7561c5 Adding GUID's to all scripts 2021-04-04 18:29:21 +00:00
silversword411
31d55d3425 Adding vscode contributing Howto to docs 2021-04-04 18:10:19 +00:00
wh1te909
171d2a5bb9 update docs 2021-04-04 09:21:17 +00:00
wh1te909
c5d05c1205 Release 0.5.0 2021-04-04 07:51:19 +00:00
wh1te909
2973e0559a bump versions 2021-04-04 07:49:27 +00:00
wh1te909
ec27288dcf add link 2021-04-04 07:47:20 +00:00
wh1te909
f92e5c7093 update docs 2021-04-04 07:37:36 +00:00
wh1te909
7c67155c49 update docs 2021-04-04 07:10:30 +00:00
wh1te909
b102cd4652 log unhashable type errors when parsing custom fields 2021-04-04 05:49:39 +00:00
wh1te909
67f9a48c37 remove version from consumers view 2021-04-04 02:09:44 +00:00
wh1te909
a0c8a1ee65 change consumers 2021-04-04 01:59:06 +00:00
wh1te909
7e7d272b06 fix update script 2021-04-04 01:41:25 +00:00
sadnub
3c642240ae fix showing default value in script variable if value doesn't exist 2021-04-03 21:37:09 -04:00
wh1te909
b5157fcaf1 update bash scripts for channels 2021-04-04 01:13:27 +00:00
sadnub
d1cb42f1bc fix nats container config path 2021-04-03 20:52:06 -04:00
sadnub
84cde1a16a fix vuex getter for community script show state 2021-04-03 20:40:15 -04:00
wh1te909
877f5db1ce update reqs 2021-04-03 23:39:46 +00:00
sadnub
787164e245 add websockets container. Fix mesh upload on new installation. remove cypress until we need it 2021-04-03 17:50:18 -04:00
wh1te909
d77fc5e7c5 isort 2021-04-03 03:36:28 +00:00
wh1te909
cca39a67d6 start channels tests 2021-04-03 03:35:41 +00:00
wh1te909
a6c9a0431a isort skip 2021-04-03 03:34:46 +00:00
wh1te909
729a80a639 switch to jsonwebsocket 2021-04-03 03:25:01 +00:00
wh1te909
31cb3001f6 Merge branch 'channels' into develop 2021-04-03 00:57:18 +00:00
wh1te909
5d0f54a329 fix typo 2021-04-03 00:50:28 +00:00
wh1te909
c8c3f5b5b7 add channels to install script 2021-04-03 00:24:31 +00:00
wh1te909
ba473ed75a fix channels in prod 2021-04-03 00:17:20 +00:00
wh1te909
7236fd59f8 more websocket work 2021-04-02 22:55:16 +00:00
wh1te909
9471e8f1fd add channels to reqs 2021-04-02 22:54:58 +00:00
Dan
a2d39b51bb Merge pull request #359 from silversword411/develop
Rename computer script - change default timeout
2021-04-02 14:10:29 -07:00
wh1te909
2920934b55 fix scripts and tests 2021-04-02 21:03:32 +00:00
silversword411
3f709d448e Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-02 20:49:55 +00:00
silversword411
b79f66183f Changing Rename Computer Default Timeout 2021-04-02 20:48:46 +00:00
silversword411
8672f57e55 Changing Rename Computer Default Timeout 2021-04-02 20:47:06 +00:00
wh1te909
1e99c82351 testing websockets with channels 2021-04-02 20:04:04 +00:00
sadnub
1a2ff851f3 remove console log 2021-04-02 15:53:39 -04:00
sadnub
f1c27c3959 fix script timeout on running favorite script 2021-04-02 15:50:12 -04:00
sadnub
b30dac0f15 add script default args and reworked the script dropdowns to include categories 2021-04-02 15:48:08 -04:00
wh1te909
cc79e5cdaf software tests 2021-04-01 19:39:45 +00:00
wh1te909
d9a3b2f2cb tests 2021-04-01 18:54:47 +00:00
wh1te909
479b528d09 more tests 2021-04-01 09:01:57 +00:00
wh1te909
461fb84fb9 add tests 2021-04-01 08:41:51 +00:00
wh1te909
bd7685e3fa update docs 2021-04-01 07:40:42 +00:00
wh1te909
cd98cb64b3 refactor some installer views 2021-04-01 07:34:34 +00:00
sadnub
0f32a3ec24 good start to script variables 2021-04-01 00:23:42 -04:00
Dan
ca446cac87 Merge pull request #357 from bbrendon/patch-1
Update Check_Events_for_Bluescreens.ps1 - indicate time frame.
2021-03-31 17:53:21 -07:00
Brendon Baumgartner
6ea907ffda Update Check_Events_for_Bluescreens.ps1
indicate time frame.
2021-03-31 17:11:45 -07:00
wh1te909
5287baa70d fix test 2021-03-31 20:46:15 +00:00
wh1te909
25935fec84 add test for spaces in script filenames 2021-03-31 20:19:18 +00:00
Dan
e855a063ff Merge pull request #356 from tremor021/develop
Added some scripts
2021-03-31 12:15:01 -07:00
tremor021
c726b8c9f0 Fixed some things 2021-03-31 21:02:36 +02:00
tremor021
13cb99290e Revert "Fixed script naming"
This reverts commit cea9413fd1.
2021-03-31 20:57:50 +02:00
tremor021
cea9413fd1 Fixed script naming 2021-03-31 20:54:07 +02:00
wh1te909
1432853b39 Release 0.4.32 2021-03-31 18:35:05 +00:00
tremor021
6d6c2b86e8 Added some scripts 2021-03-31 20:34:45 +02:00
wh1te909
77b1d964b5 bump versions 2021-03-31 18:33:43 +00:00
wh1te909
549936fc09 add logging and timeout to deployment gen 2021-03-31 18:24:27 +00:00
wh1te909
c9c32f09c5 public docs on push to master instead of develop 2021-03-31 18:11:14 +00:00
sadnub
77f7778d4a fix being ablwe to add/edit automation and alert templates on sites and clients 2021-03-31 12:03:26 -04:00
wh1te909
84b6be9364 un-hide custom fields 2021-03-31 07:29:28 +00:00
175 changed files with 6972 additions and 5473 deletions

View File

@@ -8,7 +8,7 @@ ENV VIRTUAL_ENV ${WORKSPACE_DIR}/api/tacticalrmm/env
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
EXPOSE 8000 8383
RUN groupadd -g 1000 tactical && \
useradd -u 1000 -g 1000 tactical

View File

@@ -2,6 +2,7 @@ version: '3.4'
services:
api-dev:
container_name: trmm-api-dev
image: api-dev
restart: always
build:
@@ -21,6 +22,7 @@ services:
- tactical-backend
app-dev:
container_name: trmm-app-dev
image: node:14-alpine
restart: always
command: /bin/sh -c "npm install npm@latest -g && npm install && npm run serve -- --host 0.0.0.0 --port ${APP_PORT}"
@@ -36,6 +38,7 @@ services:
# nats
nats-dev:
container_name: trmm-nats-dev
image: ${IMAGE_REPO}tactical-nats:${VERSION}
restart: always
environment:
@@ -55,6 +58,7 @@ services:
# meshcentral container
meshcentral-dev:
container_name: trmm-meshcentral-dev
image: ${IMAGE_REPO}tactical-meshcentral:${VERSION}
restart: always
environment:
@@ -77,6 +81,7 @@ services:
# mongodb container for meshcentral
mongodb-dev:
container_name: trmm-mongodb-dev
image: mongo:4.4
restart: always
environment:
@@ -92,6 +97,7 @@ services:
# postgres database for api service
postgres-dev:
container_name: trmm-postgres-dev
image: postgres:13-alpine
restart: always
environment:
@@ -107,6 +113,7 @@ services:
# redis container for celery tasks
redis-dev:
container_name: trmm-redis-dev
restart: always
image: redis:6.0-alpine
networks:
@@ -115,6 +122,7 @@ services:
- tactical-redis
init-dev:
container_name: trmm-init-dev
image: api-dev
build:
context: .
@@ -143,6 +151,7 @@ services:
# container for celery worker service
celery-dev:
container_name: trmm-celery-dev
image: api-dev
build:
context: .
@@ -160,6 +169,7 @@ services:
# container for celery beat service
celerybeat-dev:
container_name: trmm-celerybeat-dev
image: api-dev
build:
context: .
@@ -175,8 +185,29 @@ services:
- postgres-dev
- redis-dev
nginx-dev:
# container for websockets communication
websockets-dev:
container_name: trmm-websockets-dev
image: api-dev
build:
context: .
dockerfile: ./api.dockerfile
command: ["tactical-websockets-dev"]
restart: always
networks:
dev:
aliases:
- tactical-websockets
volumes:
- tactical-data-dev:/opt/tactical
- ..:/workspace:cached
depends_on:
- postgres-dev
- redis-dev
# container for tactical reverse proxy
nginx-dev:
container_name: trmm-nginx-dev
image: ${IMAGE_REPO}tactical-nginx:${VERSION}
restart: always
environment:

View File

@@ -136,10 +136,11 @@ if [ "$1" = 'tactical-init-dev' ]; then
webenv="$(cat << EOF
PROD_URL = "${HTTP_PROTOCOL}://${API_HOST}"
DEV_URL = "${HTTP_PROTOCOL}://${API_HOST}"
APP_URL = https://${APP_HOST}
APP_URL = "https://${APP_HOST}"
DOCKER_BUILD = 1
EOF
)"
echo "${webenv}" | tee ${WORKSPACE_DIR}/web/.env > /dev/null
echo "${webenv}" | tee "${WORKSPACE_DIR}"/web/.env > /dev/null
# chown everything to tactical user
chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${WORKSPACE_DIR}"
@@ -164,3 +165,8 @@ if [ "$1" = 'tactical-celerybeat-dev' ]; then
test -f "${WORKSPACE_DIR}/api/tacticalrmm/celerybeat.pid" && rm "${WORKSPACE_DIR}/api/tacticalrmm/celerybeat.pid"
"${VIRTUAL_ENV}"/bin/celery -A tacticalrmm beat -l debug
fi
if [ "$1" = 'tactical-websockets-dev' ]; then
check_tactical_ready
"${VIRTUAL_ENV}"/bin/daphne tacticalrmm.asgi:application --port 8383 -b 0.0.0.0
fi

View File

@@ -1,6 +1,7 @@
# To ensure app dependencies are ported from your virtual environment/host machine into your container, run 'pip freeze > requirements.txt' in the terminal to overwrite this file
asyncio-nats-client
celery
channels
Django
django-cors-headers
django-rest-knox
@@ -30,3 +31,5 @@ mkdocs-material
pymdown-extensions
Pygments
mypy
pysnooper
isort

View File

@@ -2,7 +2,7 @@ name: Deploy Docs
on:
push:
branches:
- develop
- master
defaults:
run:

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-11 01:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0013_user_client_tree_sort'),
]
operations = [
migrations.AddField(
model_name='user',
name='client_tree_splitter',
field=models.PositiveIntegerField(default=11),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-11 03:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0014_user_client_tree_splitter'),
]
operations = [
migrations.AddField(
model_name='user',
name='loading_bar_color',
field=models.CharField(default='red', max_length=255),
),
]

View File

@@ -36,6 +36,8 @@ class User(AbstractUser, BaseAuditModel):
client_tree_sort = models.CharField(
max_length=50, choices=CLIENT_TREE_SORT_CHOICES, default="alphafail"
)
client_tree_splitter = models.PositiveIntegerField(default=11)
loading_bar_color = models.CharField(max_length=255, default="red")
agent = models.OneToOneField(
"agents.Agent",

View File

@@ -13,6 +13,8 @@ class UserUISerializer(ModelSerializer):
"agent_dblclick_action",
"default_agent_tbl_tab",
"client_tree_sort",
"client_tree_splitter",
"loading_bar_color",
]

View File

@@ -278,6 +278,8 @@ class TestUserAction(TacticalTestCase):
"agent_dblclick_action": "editagent",
"default_agent_tbl_tab": "mixed",
"client_tree_sort": "alpha",
"client_tree_splitter": 14,
"loading_bar_color": "green",
}
r = self.client.patch(url, data, format="json")
self.assertEqual(r.status_code, 200)

View File

@@ -5,7 +5,6 @@ import time
from collections import Counter
from distutils.version import LooseVersion
from typing import Any
from django.contrib.postgres.fields import ArrayField
import msgpack
import validators
@@ -14,6 +13,7 @@ from Crypto.Hash import SHA3_384
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils import timezone as djangotime
from loguru import logger
@@ -166,7 +166,7 @@ class Agent(BaseAuditModel):
@property
def checks(self):
total, passing, failing = 0, 0, 0
total, passing, failing, warning, info = 0, 0, 0, 0, 0
if self.agentchecks.exists(): # type: ignore
for i in self.agentchecks.all(): # type: ignore
@@ -174,13 +174,20 @@ class Agent(BaseAuditModel):
if i.status == "passing":
passing += 1
elif i.status == "failing":
failing += 1
if i.alert_severity == "error":
failing += 1
elif i.alert_severity == "warning":
warning += 1
elif i.alert_severity == "info":
info += 1
ret = {
"total": total,
"passing": passing,
"failing": failing,
"has_failing_checks": failing > 0,
"warning": warning,
"info": info,
"has_failing_checks": failing > 0 or warning > 0,
}
return ret
@@ -195,6 +202,27 @@ class Agent(BaseAuditModel):
except:
return ["unknown cpu model"]
@property
def graphics(self):
ret, mrda = [], []
try:
graphics = self.wmi_detail["graphics"]
for i in graphics:
caption = [x["Caption"] for x in i if "Caption" in x][0]
if "microsoft remote display adapter" in caption.lower():
mrda.append("yes")
continue
ret.append([x["Caption"] for x in i if "Caption" in x][0])
# only return this if no other graphics cards
if not ret and mrda:
return "Microsoft Remote Display Adapter"
return ", ".join(ret)
except:
return "Graphics info requires agent v1.4.14"
@property
def local_ips(self):
ret = []
@@ -296,10 +324,13 @@ class Agent(BaseAuditModel):
from scripts.models import Script
script = Script.objects.get(pk=scriptpk)
parsed_args = script.parse_script_args(self, script.shell, args)
data = {
"func": "runscriptfull" if full else "runscript",
"timeout": timeout,
"script_args": args,
"script_args": parsed_args,
"payload": {
"code": script.code,
"shell": script.shell,
@@ -319,7 +350,7 @@ class Agent(BaseAuditModel):
online = [
agent
for agent in Agent.objects.only(
"pk", "last_seen", "overdue_time", "offline_time"
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
)
if agent.status == "online"
]
@@ -648,7 +679,11 @@ class Agent(BaseAuditModel):
except ErrTimeout:
ret = "timeout"
else:
ret = msgpack.loads(msg.data) # type: ignore
try:
ret = msgpack.loads(msg.data) # type: ignore
except Exception as e:
logger.error(e)
ret = str(e)
await nc.close()
return ret
@@ -812,12 +847,6 @@ class RecoveryAction(models.Model):
def __str__(self):
return f"{self.agent.hostname} - {self.mode}"
def send(self):
ret = {"recovery": self.mode}
if self.mode == "command":
ret["cmd"] = self.command
return ret
class Note(models.Model):
agent = models.ForeignKey(

View File

@@ -16,6 +16,7 @@ class AgentSerializer(serializers.ModelSerializer):
local_ips = serializers.ReadOnlyField()
make_model = serializers.ReadOnlyField()
physical_disks = serializers.ReadOnlyField()
graphics = serializers.ReadOnlyField()
checks = serializers.ReadOnlyField()
timezone = serializers.ReadOnlyField()
all_timezones = serializers.SerializerMethodField()

View File

@@ -1,9 +1,7 @@
import asyncio
import datetime as dt
import json
import random
import subprocess
import tempfile
import urllib.parse
from time import sleep
from typing import Union
@@ -13,21 +11,21 @@ from loguru import logger
from packaging import version as pyver
from agents.models import Agent
from core.models import CoreSettings
from core.models import CoreSettings, CodeSignToken
from logs.models import PendingAction
from scripts.models import Script
from tacticalrmm.celery import app
from tacticalrmm.utils import run_nats_api_cmd
logger.configure(**settings.LOG_CONFIG)
def agent_update(pk: int) -> str:
def agent_update(pk: int, codesigntoken: str = None) -> str:
from agents.utils import get_exegen_url
agent = Agent.objects.get(pk=pk)
if pyver.parse(agent.version) <= pyver.parse("1.1.11"):
logger.warning(
f"{agent.hostname} v{agent.version} is running an unsupported version. Refusing to auto update."
)
if pyver.parse(agent.version) <= pyver.parse("1.3.0"):
return "not supported"
# skip if we can't determine the arch
@@ -37,18 +35,15 @@ def agent_update(pk: int) -> str:
)
return "noarch"
# removed sqlite in 1.4.0 to get rid of cgo dependency
# 1.3.0 has migration func to move from sqlite to win registry, so force an upgrade to 1.3.0 if old agent
if pyver.parse(agent.version) >= pyver.parse("1.3.0"):
version = settings.LATEST_AGENT_VER
url = agent.winagent_dl
inno = agent.win_inno_exe
version = settings.LATEST_AGENT_VER
inno = agent.win_inno_exe
if codesigntoken is not None and pyver.parse(version) >= pyver.parse("1.5.0"):
base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {"version": version, "arch": agent.arch, "token": codesigntoken}
url = base_url + urllib.parse.urlencode(params)
else:
version = "1.3.0"
inno = (
"winagent-v1.3.0.exe" if agent.arch == "64" else "winagent-v1.3.0-x86.exe"
)
url = f"https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/{inno}"
url = agent.winagent_dl
if agent.pendingactions.filter(
action_type="agentupdate", status="pending"
@@ -81,10 +76,15 @@ def agent_update(pk: int) -> str:
@app.task
def send_agent_update_task(pks: list[int]) -> None:
try:
codesigntoken = CodeSignToken.objects.first().token
except:
codesigntoken = None
chunks = (pks[i : i + 30] for i in range(0, len(pks), 30))
for chunk in chunks:
for pk in chunk:
agent_update(pk)
agent_update(pk, codesigntoken)
sleep(0.05)
sleep(4)
@@ -95,6 +95,11 @@ def auto_self_agent_update_task() -> None:
if not core.agent_auto_update:
return
try:
codesigntoken = CodeSignToken.objects.first().token
except:
codesigntoken = None
q = Agent.objects.only("pk", "version")
pks: list[int] = [
i.pk
@@ -105,7 +110,7 @@ def auto_self_agent_update_task() -> None:
chunks = (pks[i : i + 30] for i in range(0, len(pks), 30))
for chunk in chunks:
for pk in chunk:
agent_update(pk)
agent_update(pk, codesigntoken)
sleep(0.05)
sleep(4)
@@ -257,30 +262,13 @@ def run_script_email_results_task(
logger.error(e)
def _get_nats_config() -> dict:
return {
"key": settings.SECRET_KEY,
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
}
@app.task
def monitor_agents_task() -> None:
agents = Agent.objects.only(
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
)
ret = [i.agent_id for i in agents if i.status != "online"]
config = _get_nats_config()
config["agents"] = ret
with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, "w") as f:
json.dump(config, f)
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", "monitor"]
try:
subprocess.run(cmd, capture_output=True, timeout=30)
except Exception as e:
logger.error(e)
ids = [i.agent_id for i in agents if i.status != "online"]
run_nats_api_cmd("monitor", ids)
@app.task
@@ -288,15 +276,5 @@ def get_wmi_task() -> None:
agents = Agent.objects.only(
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
)
ret = [i.agent_id for i in agents if i.status == "online"]
config = _get_nats_config()
config["agents"] = ret
with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, "w") as f:
json.dump(config, f)
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", "wmi"]
try:
subprocess.run(cmd, capture_output=True, timeout=30)
except Exception as e:
logger.error(e)
ids = [i.agent_id for i in agents if i.status == "online"]
run_nats_api_cmd("wmi", ids)

View File

@@ -364,7 +364,7 @@ class TestAgentViews(TacticalTestCase):
@patch("os.path.exists")
def test_install_agent(self, mock_file_exists):
url = f"/agents/installagent/"
url = "/agents/installagent/"
site = baker.make("clients.Site")
data = {
@@ -372,12 +372,13 @@ class TestAgentViews(TacticalTestCase):
"site": site.id, # type: ignore
"arch": "64",
"expires": 23,
"installMethod": "exe",
"installMethod": "manual",
"api": "https://api.example.com",
"agenttype": "server",
"rdp": 1,
"ping": 0,
"power": 0,
"fileName": "rmm-client-site-server.exe",
}
mock_file_exists.return_value = False
@@ -393,7 +394,6 @@ class TestAgentViews(TacticalTestCase):
r = self.client.post(url, data, format="json")
self.assertEqual(r.status_code, 415)
data["installMethod"] = "manual"
data["arch"] = "64"
mock_file_exists.return_value = True
r = self.client.post(url, data, format="json")
@@ -405,6 +405,9 @@ class TestAgentViews(TacticalTestCase):
self.assertIn("power", r.json()["cmd"])
self.assertIn("ping", r.json()["cmd"])
data["installMethod"] = "powershell"
self.assertEqual(r.status_code, 200)
self.check_not_authenticated("post", url)
@patch("agents.models.Agent.nats_cmd")
@@ -840,7 +843,7 @@ class TestAgentViewsNew(TacticalTestCase):
self.authenticate()
self.setup_coresettings()
def test_agent_counts(self):
""" def test_agent_counts(self):
url = "/agents/agent_counts/"
# create some data
@@ -867,7 +870,7 @@ class TestAgentViewsNew(TacticalTestCase):
self.assertEqual(r.status_code, 200)
self.assertEqual(r.data, data) # type: ignore
self.check_not_authenticated("post", url)
self.check_not_authenticated("post", url) """
def test_agent_maintenance_mode(self):
url = "/agents/maintenance/"
@@ -911,8 +914,9 @@ class TestAgentTasks(TacticalTestCase):
self.authenticate()
self.setup_coresettings()
@patch("agents.utils.get_exegen_url")
@patch("agents.models.Agent.nats_cmd")
def test_agent_update(self, nats_cmd):
def test_agent_update(self, nats_cmd, get_exe):
from agents.tasks import agent_update
agent_noarch = baker.make_recipe(
@@ -923,63 +927,96 @@ class TestAgentTasks(TacticalTestCase):
r = agent_update(agent_noarch.pk)
self.assertEqual(r, "noarch")
agent_1111 = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.1.11",
)
r = agent_update(agent_1111.pk)
self.assertEqual(r, "not supported")
agent64_1112 = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.1.12",
)
r = agent_update(agent64_1112.pk)
self.assertEqual(r, "created")
action = PendingAction.objects.get(agent__pk=agent64_1112.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")
self.assertEqual(
action.details["url"],
"https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/winagent-v1.3.0.exe",
)
self.assertEqual(action.details["inno"], "winagent-v1.3.0.exe")
self.assertEqual(action.details["version"], "1.3.0")
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": "https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/winagent-v1.3.0.exe",
"version": "1.3.0",
"inno": "winagent-v1.3.0.exe",
},
},
wait=False,
)
agent_64_130 = baker.make_recipe(
agent_130 = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.3.0",
)
nats_cmd.return_value = "ok"
r = agent_update(agent_64_130.pk)
r = agent_update(agent_130.pk)
self.assertEqual(r, "not supported")
# test __without__ code signing
agent64_nosign = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.4.14",
)
r = agent_update(agent64_nosign.pk, None)
self.assertEqual(r, "created")
action = PendingAction.objects.get(agent__pk=agent64_nosign.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")
self.assertEqual(
action.details["url"],
f"https://github.com/wh1te909/rmmagent/releases/download/v{settings.LATEST_AGENT_VER}/winagent-v{settings.LATEST_AGENT_VER}.exe",
)
self.assertEqual(
action.details["inno"], f"winagent-v{settings.LATEST_AGENT_VER}.exe"
)
self.assertEqual(action.details["version"], settings.LATEST_AGENT_VER)
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": settings.DL_64,
"url": f"https://github.com/wh1te909/rmmagent/releases/download/v{settings.LATEST_AGENT_VER}/winagent-v{settings.LATEST_AGENT_VER}.exe",
"version": settings.LATEST_AGENT_VER,
"inno": f"winagent-v{settings.LATEST_AGENT_VER}.exe",
},
},
wait=False,
)
action = PendingAction.objects.get(agent__pk=agent_64_130.pk)
# test __with__ code signing (64 bit)
codesign = baker.make("core.CodeSignToken", token="testtoken123")
agent64_sign = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.4.14",
)
nats_cmd.return_value = "ok"
get_exe.return_value = "https://exe.tacticalrmm.io"
r = agent_update(agent64_sign.pk, codesign.token) # type: ignore
self.assertEqual(r, "created")
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": f"https://exe.tacticalrmm.io/api/v1/winagents/?version={settings.LATEST_AGENT_VER}&arch=64&token=testtoken123", # type: ignore
"version": settings.LATEST_AGENT_VER,
"inno": f"winagent-v{settings.LATEST_AGENT_VER}.exe",
},
},
wait=False,
)
action = PendingAction.objects.get(agent__pk=agent64_sign.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")
# test __with__ code signing (32 bit)
agent32_sign = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 32 bit (build 19041.450)",
version="1.4.14",
)
nats_cmd.return_value = "ok"
get_exe.return_value = "https://exe.tacticalrmm.io"
r = agent_update(agent32_sign.pk, codesign.token) # type: ignore
self.assertEqual(r, "created")
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": f"https://exe.tacticalrmm.io/api/v1/winagents/?version={settings.LATEST_AGENT_VER}&arch=32&token=testtoken123", # type: ignore
"version": settings.LATEST_AGENT_VER,
"inno": f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe",
},
},
wait=False,
)
action = PendingAction.objects.get(agent__pk=agent32_sign.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")

View File

@@ -27,7 +27,6 @@ urlpatterns = [
path("<int:pk>/notes/", views.GetAddNotes.as_view()),
path("<int:pk>/note/", views.GetEditDeleteNote.as_view()),
path("bulk/", views.bulk),
path("agent_counts/", views.agent_counts),
path("maintenance/", views.agent_maintenance),
path("<int:pk>/wmi/", views.WMI.as_view()),
]

View File

@@ -0,0 +1,37 @@
import random
import requests
import urllib.parse
from django.conf import settings
def get_exegen_url() -> str:
urls: list[str] = settings.EXE_GEN_URLS
for url in urls:
try:
r = requests.get(url, timeout=10)
except:
continue
if r.status_code == 200:
return url
return random.choice(urls)
def get_winagent_url(arch: str) -> str:
from core.models import CodeSignToken
try:
codetoken = CodeSignToken.objects.first().token
base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {
"version": settings.LATEST_AGENT_VER,
"arch": arch,
"token": codetoken,
}
dl_url = base_url + urllib.parse.urlencode(params)
except:
dl_url = settings.DL_64 if arch == "64" else settings.DL_32
return dl_url

View File

@@ -18,11 +18,7 @@ from core.models import CoreSettings
from logs.models import AuditLog, PendingAction
from scripts.models import Script
from scripts.tasks import handle_bulk_command_task, handle_bulk_script_task
from tacticalrmm.utils import (
get_default_timezone,
notify_error,
reload_nats,
)
from tacticalrmm.utils import get_default_timezone, notify_error, reload_nats
from winupdate.serializers import WinUpdatePolicySerializer
from winupdate.tasks import bulk_check_for_updates_task, bulk_install_updates_task
@@ -44,7 +40,7 @@ logger.configure(**settings.LOG_CONFIG)
@api_view()
def get_agent_versions(request):
agents = Agent.objects.only("pk")
agents = Agent.objects.prefetch_related("site").only("pk", "hostname")
return Response(
{
"versions": [settings.LATEST_AGENT_VER],
@@ -87,7 +83,7 @@ def uninstall(request):
return Response(f"{name} will now be uninstalled.")
@api_view(["PATCH"])
@api_view(["PATCH", "PUT"])
def edit_agent(request):
agent = get_object_or_404(Agent, pk=request.data["id"])
@@ -357,6 +353,7 @@ class Reboot(APIView):
@api_view(["POST"])
def install_agent(request):
from agents.utils import get_winagent_url
from knox.models import AuthToken
client_id = request.data["client"]
@@ -379,22 +376,27 @@ def install_agent(request):
inno = (
f"winagent-v{version}.exe" if arch == "64" else f"winagent-v{version}-x86.exe"
)
download_url = settings.DL_64 if arch == "64" else settings.DL_32
goarch = "amd64" if arch == "64" else "386"
download_url = get_winagent_url(arch)
_, token = AuthToken.objects.create(
user=request.user, expiry=dt.timedelta(hours=request.data["expires"])
)
if request.data["installMethod"] == "exe":
ret = {
"token": token,
"url": download_url,
"inno": inno,
"goarch": goarch,
"genurl": settings.EXE_GEN_URL,
}
return Response(ret)
from tacticalrmm.utils import generate_winagent_exe
return generate_winagent_exe(
client=client_id,
site=site_id,
agent_type=request.data["agenttype"],
rdp=request.data["rdp"],
ping=request.data["ping"],
power=request.data["power"],
arch=arch,
token=token,
api=request.data["api"],
file_name=request.data["fileName"],
)
elif request.data["installMethod"] == "manual":
cmd = [
@@ -670,49 +672,6 @@ def bulk(request):
return notify_error("Something went wrong")
@api_view(["POST"])
def agent_counts(request):
server_offline_count = len(
[
agent
for agent in Agent.objects.filter(monitoring_type="server").only(
"pk",
"last_seen",
"overdue_time",
"offline_time",
)
if not agent.status == "online"
]
)
workstation_offline_count = len(
[
agent
for agent in Agent.objects.filter(monitoring_type="workstation").only(
"pk",
"last_seen",
"overdue_time",
"offline_time",
)
if not agent.status == "online"
]
)
return Response(
{
"total_server_count": Agent.objects.filter(
monitoring_type="server"
).count(),
"total_server_offline_count": server_offline_count,
"total_workstation_count": Agent.objects.filter(
monitoring_type="workstation"
).count(),
"total_workstation_offline_count": workstation_offline_count,
}
)
@api_view(["POST"])
def agent_maintenance(request):
if request.data["type"] == "Client":

View File

@@ -633,12 +633,15 @@ class Check(BaseAuditModel):
if self.error_threshold:
text += f" Error Threshold: {self.error_threshold}%"
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
try:
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
body = subject + f" - Free: {percent_free}%, {text}"
body = subject + f" - Free: {percent_free}%, {text}"
except:
body = subject + f" - Disk {self.disk} does not exist"
elif self.check_type == "script":
@@ -667,16 +670,7 @@ class Check(BaseAuditModel):
body = subject + f" - Average memory usage: {avg}%, {text}"
elif self.check_type == "winsvc":
try:
status = list(
filter(lambda x: x["name"] == self.svc_name, self.agent.services)
)[0]["status"]
# catch services that don't exist if policy check
except:
status = "Unknown"
body = subject + f" - Status: {status.upper()}"
body = subject + f" - Status: {self.more_info}"
elif self.check_type == "eventlog":
@@ -719,11 +713,15 @@ class Check(BaseAuditModel):
if self.error_threshold:
text += f" Error Threshold: {self.error_threshold}%"
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
body = subject + f" - Free: {percent_free}%, {text}"
try:
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
body = subject + f" - Free: {percent_free}%, {text}"
except:
body = subject + f" - Disk {self.disk} does not exist"
elif self.check_type == "script":
body = subject + f" - Return code: {self.retcode}"
elif self.check_type == "ping":
@@ -741,10 +739,7 @@ class Check(BaseAuditModel):
elif self.check_type == "memory":
body = subject + f" - Average memory usage: {avg}%, {text}"
elif self.check_type == "winsvc":
status = list(
filter(lambda x: x["name"] == self.svc_name, self.agent.services)
)[0]["status"]
body = subject + f" - Status: {status.upper()}"
body = subject + f" - Status: {self.more_info}"
elif self.check_type == "eventlog":
body = subject

View File

@@ -86,7 +86,10 @@ class GetUpdateDeleteCheck(APIView):
check = get_object_or_404(Check, pk=pk)
# remove fields that should not be changed when editing a check from the frontend
if "check_alert" not in request.data.keys():
if (
"check_alert" not in request.data.keys()
and "check_reset" not in request.data.keys()
):
[request.data.pop(i) for i in check.non_editable_fields]
# set event id to 0 if wildcard because it needs to be an integer field for db
@@ -108,6 +111,11 @@ class GetUpdateDeleteCheck(APIView):
if check.policy:
update_policy_check_fields_task(checkpk=pk)
# resolve any alerts that are open
if "check_reset" in request.data.keys():
if obj.alert.filter(resolved=False).exists():
obj.alert.get(resolved=False).resolve()
return Response(f"{obj.readable_desc} was edited!")
def delete(self, request, pk):

View File

@@ -1,7 +1,7 @@
import uuid
from django.db import models
from django.contrib.postgres.fields import ArrayField
from django.db import models
from agents.models import Agent
from logs.models import BaseAuditModel
@@ -86,16 +86,24 @@ class Client(BaseAuditModel):
.prefetch_related("agentchecks")
)
failing = 0
data = {"error": False, "warning": False}
for agent in agents:
if agent.checks["has_failing_checks"]:
failing += 1
if agent.checks["warning"]:
data["warning"] = True
if agent.checks["failing"]:
data["error"] = True
break
if agent.overdue_email_alert or agent.overdue_text_alert:
if agent.status == "overdue":
failing += 1
data["error"] = True
break
return failing > 0
return data
@staticmethod
def serialize(client):
@@ -184,16 +192,24 @@ class Site(BaseAuditModel):
.prefetch_related("agentchecks")
)
failing = 0
data = {"error": False, "warning": False}
for agent in agents:
if agent.checks["has_failing_checks"]:
failing += 1
if agent.checks["warning"]:
data["warning"] = True
if agent.checks["failing"]:
data["error"] = True
break
if agent.overdue_email_alert or agent.overdue_text_alert:
if agent.status == "overdue":
failing += 1
data["error"] = True
break
return failing > 0
return data
@staticmethod
def serialize(site):

View File

@@ -33,6 +33,7 @@ class SiteSerializer(ModelSerializer):
"name",
"server_policy",
"workstation_policy",
"alert_template",
"client_name",
"client",
"custom_fields",
@@ -75,6 +76,7 @@ class ClientSerializer(ModelSerializer):
"name",
"server_policy",
"workstation_policy",
"alert_template",
"sites",
"custom_fields",
)
@@ -93,7 +95,6 @@ class SiteTreeSerializer(ModelSerializer):
class Meta:
model = Site
fields = "__all__"
ordering = ("failing_checks",)
class ClientTreeSerializer(ModelSerializer):
@@ -104,7 +105,6 @@ class ClientTreeSerializer(ModelSerializer):
class Meta:
model = Client
fields = "__all__"
ordering = ("failing_checks",)
class DeploymentSerializer(ModelSerializer):

View File

@@ -6,6 +6,7 @@ import pytz
from django.conf import settings
from django.shortcuts import get_object_or_404
from django.utils import timezone as djangotime
from loguru import logger
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
@@ -24,6 +25,8 @@ from .serializers import (
SiteSerializer,
)
logger.configure(**settings.LOG_CONFIG)
class GetAddClients(APIView):
def get(self, request):
@@ -167,13 +170,15 @@ class GetUpdateSite(APIView):
def put(self, request, pk):
site = get_object_or_404(Site, pk=pk)
if (
if "client" in request.data["site"].keys() and (
site.client.id != request.data["site"]["client"]
and site.client.sites.count() == 1
):
return notify_error("A client must have at least one site")
serializer = SiteSerializer(instance=site, data=request.data["site"])
serializer = SiteSerializer(
instance=site, data=request.data["site"], partial=True
)
serializer.is_valid(raise_exception=True)
serializer.save()
@@ -278,9 +283,7 @@ class GenerateAgent(APIView):
permission_classes = (AllowAny,)
def get(self, request, uid):
import requests
import tempfile
from django.http import FileResponse
from tacticalrmm.utils import generate_winagent_exe
try:
_ = uuid.UUID(uid, version=4)
@@ -289,11 +292,6 @@ class GenerateAgent(APIView):
d = get_object_or_404(Deployment, uid=uid)
inno = (
f"winagent-v{settings.LATEST_AGENT_VER}.exe"
if d.arch == "64"
else f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe"
)
client = d.client.name.replace(" ", "").lower()
site = d.site.name.replace(" ", "").lower()
client = re.sub(r"([^a-zA-Z0-9]+)", "", client)
@@ -301,34 +299,15 @@ class GenerateAgent(APIView):
ext = ".exe" if d.arch == "64" else "-x86.exe"
file_name = f"rmm-{client}-{site}-{d.mon_type}{ext}"
data = {
"client": d.client.pk,
"site": d.site.pk,
"agenttype": d.mon_type,
"rdp": str(d.install_flags["rdp"]),
"ping": str(d.install_flags["ping"]),
"power": str(d.install_flags["power"]),
"goarch": "amd64" if d.arch == "64" else "386",
"token": d.token_key,
"inno": inno,
"url": settings.DL_64 if d.arch == "64" else settings.DL_32,
"api": f"https://{request.get_host()}",
}
headers = {"Content-type": "application/json"}
with tempfile.NamedTemporaryFile() as fp:
r = requests.post(
settings.EXE_GEN_URL,
json=data,
headers=headers,
stream=True,
)
with open(fp.name, "wb") as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
del r
response = FileResponse(
open(fp.name, "rb"), as_attachment=True, filename=file_name
)
return response
return generate_winagent_exe(
client=d.client.pk,
site=d.site.pk,
agent_type=d.mon_type,
rdp=d.install_flags["rdp"],
ping=d.install_flags["ping"],
power=d.install_flags["power"],
arch=d.arch,
token=d.token_key,
api=f"https://{request.get_host()}",
file_name=file_name,
)

View File

@@ -1,6 +1,7 @@
from django.contrib import admin
from .models import CoreSettings, CustomField
from .models import CoreSettings, CustomField, CodeSignToken
admin.site.register(CoreSettings)
admin.site.register(CustomField)
admin.site.register(CodeSignToken)

View File

@@ -0,0 +1,79 @@
import asyncio
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from django.contrib.auth.models import AnonymousUser
from agents.models import Agent
class DashInfo(AsyncJsonWebsocketConsumer):
async def connect(self):
self.user = self.scope["user"]
if isinstance(self.user, AnonymousUser):
await self.close()
await self.accept()
self.connected = True
self.dash_info = asyncio.create_task(self.send_dash_info())
async def disconnect(self, close_code):
try:
self.dash_info.cancel()
except:
pass
self.connected = False
await self.close()
async def receive(self, json_data=None):
pass
@database_sync_to_async
def get_dashboard_info(self):
server_offline_count = len(
[
agent
for agent in Agent.objects.filter(monitoring_type="server").only(
"pk",
"last_seen",
"overdue_time",
"offline_time",
)
if not agent.status == "online"
]
)
workstation_offline_count = len(
[
agent
for agent in Agent.objects.filter(monitoring_type="workstation").only(
"pk",
"last_seen",
"overdue_time",
"offline_time",
)
if not agent.status == "online"
]
)
ret = {
"total_server_offline_count": server_offline_count,
"total_workstation_offline_count": workstation_offline_count,
"total_server_count": Agent.objects.filter(
monitoring_type="server"
).count(),
"total_workstation_count": Agent.objects.filter(
monitoring_type="workstation"
).count(),
}
return ret
async def send_dash_info(self):
while self.connected:
c = await self.get_dashboard_info()
await self.send_json(c)
await asyncio.sleep(30)

View File

@@ -10,6 +10,8 @@ $ping = pingchange
$auth = '"tokenchange"'
$downloadlink = 'downloadchange'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$serviceName = 'tacticalagent'
If (Get-Service $serviceName -ErrorAction SilentlyContinue) {
write-host ('Tactical RMM Is Already Installed')

View File

@@ -0,0 +1,20 @@
# Generated by Django 3.2 on 2021-04-13 05:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0018_auto_20210329_1709'),
]
operations = [
migrations.CreateModel(
name='CodeSignToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.CharField(blank=True, max_length=255, null=True)),
],
),
]

View File

@@ -266,3 +266,16 @@ class CustomField(models.Model):
return self.default_value_bool
else:
return self.default_value_string
class CodeSignToken(models.Model):
token = models.CharField(max_length=255, null=True, blank=True)
def save(self, *args, **kwargs):
if not self.pk and CodeSignToken.objects.exists():
raise ValidationError("There can only be one CodeSignToken instance")
super(CodeSignToken, self).save(*args, **kwargs)
def __str__(self):
return "Code signing token"

View File

@@ -1,7 +1,7 @@
import pytz
from rest_framework import serializers
from .models import CoreSettings, CustomField
from .models import CoreSettings, CustomField, CodeSignToken
class CoreSettingsSerializer(serializers.ModelSerializer):
@@ -27,3 +27,9 @@ class CustomFieldSerializer(serializers.ModelSerializer):
class Meta:
model = CustomField
fields = "__all__"
class CodeSignTokenSerializer(serializers.ModelSerializer):
class Meta:
model = CodeSignToken
fields = "__all__"

View File

@@ -1,14 +1,63 @@
import requests
from unittest.mock import patch
from channels.db import database_sync_to_async
from channels.testing import WebsocketCommunicator
from model_bakery import baker
from tacticalrmm.test import TacticalTestCase
from .consumers import DashInfo
from .models import CoreSettings, CustomField
from .serializers import CustomFieldSerializer
from .tasks import core_maintenance_tasks
class TestCodeSign(TacticalTestCase):
def setUp(self):
self.setup_coresettings()
self.authenticate()
self.url = "/core/codesign/"
def test_get_codesign(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.check_not_authenticated("get", self.url)
@patch("requests.post")
def test_edit_codesign_timeout(self, mock_post):
mock_post.side_effect = requests.exceptions.ConnectionError()
data = {"token": "token123"}
r = self.client.patch(self.url, data, format="json")
self.assertEqual(r.status_code, 400)
self.check_not_authenticated("patch", self.url)
class TestConsumers(TacticalTestCase):
def setUp(self):
self.setup_coresettings()
self.authenticate()
@database_sync_to_async
def get_token(self):
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=self.john)
return token.key
async def test_dash_info(self):
key = self.get_token()
communicator = WebsocketCommunicator(
DashInfo.as_asgi(), f"/ws/dashinfo/?access_token={key}"
)
communicator.scope["user"] = self.john
connected, _ = await communicator.connect()
assert connected
await communicator.disconnect()
class TestCoreTasks(TacticalTestCase):
def setUp(self):
self.setup_coresettings()

View File

@@ -12,4 +12,5 @@ urlpatterns = [
path("servermaintenance/", views.server_maintenance),
path("customfields/", views.GetAddCustomFields.as_view()),
path("customfields/<int:pk>/", views.GetUpdateDeleteCustomFields.as_view()),
path("codesign/", views.CodeSign.as_view()),
]

View File

@@ -11,8 +11,12 @@ from rest_framework.views import APIView
from tacticalrmm.utils import notify_error
from .models import CoreSettings, CustomField
from .serializers import CoreSettingsSerializer, CustomFieldSerializer
from .models import CoreSettings, CustomField, CodeSignToken
from .serializers import (
CoreSettingsSerializer,
CustomFieldSerializer,
CodeSignTokenSerializer,
)
class UploadMeshAgent(APIView):
@@ -65,6 +69,8 @@ def dashboard_info(request):
"dbl_click_action": request.user.agent_dblclick_action,
"default_agent_tbl_tab": request.user.default_agent_tbl_tab,
"client_tree_sort": request.user.client_tree_sort,
"client_tree_splitter": request.user.client_tree_splitter,
"loading_bar_color": request.user.loading_bar_color,
}
)
@@ -177,3 +183,48 @@ class GetUpdateDeleteCustomFields(APIView):
get_object_or_404(CustomField, pk=pk).delete()
return Response("ok")
class CodeSign(APIView):
def get(self, request):
token = CodeSignToken.objects.first()
return Response(CodeSignTokenSerializer(token).data)
def patch(self, request):
import requests
errors = []
for url in settings.EXE_GEN_URLS:
try:
r = requests.post(
f"{url}/api/v1/checktoken",
json={"token": request.data["token"]},
headers={"Content-type": "application/json"},
timeout=15,
)
except Exception as e:
errors.append(str(e))
else:
errors = []
break
if errors:
return notify_error(", ".join(errors))
if r.status_code == 400 or r.status_code == 401: # type: ignore
return notify_error(r.json()["ret"]) # type: ignore
elif r.status_code == 200: # type: ignore
t = CodeSignToken.objects.first()
if t is None:
CodeSignToken.objects.create(token=request.data["token"])
else:
serializer = CodeSignTokenSerializer(instance=t, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response("Token was saved")
try:
ret = r.json()["ret"] # type: ignore
except:
ret = "Something went wrong"
return notify_error(ret)

View File

@@ -1,14 +1,13 @@
amqp==5.0.5
asgiref==3.3.1
asgiref==3.3.4
asyncio-nats-client==0.11.4
billiard==3.6.3.0
celery==5.0.5
certifi==2020.12.5
cffi==1.14.5
channels==3.0.3
chardet==4.0.0
cryptography==3.4.7
decorator==4.4.2
Django==3.1.7
daphne==3.0.2
Django==3.2.0
django-cors-headers==3.7.0
django-rest-knox==4.1.0
djangorestframework==3.12.4
@@ -28,7 +27,7 @@ redis==3.5.3
requests==2.25.1
six==1.15.0
sqlparse==0.4.1
twilio==6.55.0
twilio==6.56.0
urllib3==1.26.4
uWSGI==2.0.19.1
validators==0.18.2

View File

@@ -1,254 +1,592 @@
[
{
"filename": "ClearFirefoxCache.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Clear Firefox Cache",
"description": "This script will clean up Mozilla Firefox for all users.",
"shell": "powershell"
},
{
"filename": "ClearGoogleChromeCache.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Clear Google Chrome Cache",
"description": "This script will clean up Google Chrome for all users.",
"shell": "powershell"
},
{
"filename": "InstallAdobeReader.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Install Adobe Reader DC",
"description": "Installs Adobe Reader DC.",
"shell": "powershell"
},
{
"filename": "Win_Install_Duplicati.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Install Duplicati",
"description": "This script installs Duplicati 2.0.5.1 as a service.",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software"
},
{
"filename": "Reset-WindowsUpdate.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Reset Windows Update",
"description": "This script will reset all of the Windows Updates components to DEFAULT SETTINGS.",
"shell": "powershell"
},
{
"filename": "Start-Cleanup.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Cleanup C: drive",
"description": "Cleans the C: drive's Window Temperary files, Windows SoftwareDistribution folder, the local users Temperary folder, IIS logs (if applicable) and empties the recycling bin. All deleted files will go into a log transcript in $env:TEMP. By default this script leaves files that are newer than 7 days old however this variable can be edited.",
"shell": "powershell"
},
{
"filename": "WindowsDefenderFullScanBackground.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Windows Defender Full Scan",
"description": "Runs a Windows Defender Full background scan.",
"shell": "powershell"
},
{
"filename": "WindowsDefenderQuickScanBackground.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Windows Defender Quick Scan",
"description": "Runs a Quick Scan using Windows Defender in the Background.",
"shell": "powershell"
},
{
"filename": "speedtest.py",
"submittedBy": "https://github.com/wh1te909",
"name": "Speed Test",
"description": "Runs a Speed Test",
"shell": "python"
},
{
"filename": "Rename-Installed-App.ps1",
"submittedBy": "https://github.com/bradhawkins85",
"name": "Rename Tactical RMM Agent",
"description": "Updates the DisplayName registry entry for the Tactical RMM windows agent to your desired name. This script takes 1 required argument: the name you wish to set.",
"shell": "powershell"
},
{
"filename": "bitlocker_encrypted_drive_c.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Check C Drive for Bitlocker Status",
"description": "Runs a check on drive C for Bitlocker status.",
"shell": "powershell"
},
{
"filename": "Win_Bitlocker_Create_Status_Report.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Create Bitlocker Status Report",
"description": "Creates a Bitlocker status report.",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"filename": "bitlocker_retrieve_status_report.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Retreive Bitlocker Status Report",
"description": "Retreives a Bitlocker status report.",
"shell": "powershell"
},
{
"filename": "Win_Bios_Check.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Check BIOS Information",
"description": "Retreives and reports on BIOS make, version, and date.",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"filename": "ResetHighPerformancePowerProfiletoDefaults.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Reset High Perf Power Profile",
"description": "Resets monitor, disk, standby, and hibernate timers in the default High Performance power profile to their default values. It also re-indexes the AC and DC power profiles into their default order.",
"shell": "powershell"
},
{
"filename": "SetHighPerformancePowerProfile.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Set High Perf Power Profile",
"description": "Sets the High Performance Power profile to the active power profile. Use this to keep machines from falling asleep.",
"shell": "powershell"
},
{
"filename": "Windows10Upgrade.ps1",
"submittedBy": "https://github.com/RVL-Solutions and https://github.com/darimm",
"name": "Windows 10 Upgrade",
"description": "Forces an upgrade to the latest release of Windows 10.",
"shell": "powershell"
},
{
"filename": "DiskStatus.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Check Disks",
"description": "Checks local disks for errors reported in event viewer within the last 24 hours",
"shell": "powershell"
},
{
"filename": "DuplicatiStatus.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Check Duplicati",
"description": "Checks Duplicati Backup is running properly over the last 24 hours",
"shell": "powershell"
},
{
"filename": "EnableDefender.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Enable Windows Defender",
"description": "Enables Windows Defender and sets preferences",
"shell": "powershell"
},
{
"filename": "OpenSSHServerInstall.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Install SSH",
"description": "Installs and enabled OpenSSH Server",
"shell": "powershell"
},
{
"filename": "RDP_enable.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "Enable RDP",
"description": "Enables RDP",
"shell": "cmd"
},
{
"filename": "Speedtest.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "PS Speed Test",
"description": "Powershell speed test (win 10 or server2016+)",
"shell": "powershell"
},
{
"filename": "SyncTime.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "Sync DC Time",
"description": "Syncs time with domain controller",
"shell": "cmd"
},
{
"filename": "WinDefenderClearLogs.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Clear Defender Logs",
"description": "Clears Windows Defender Logs",
"shell": "powershell"
},
{
"filename": "WinDefenderStatus.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Defender Status",
"description": "This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours",
"shell": "powershell"
},
{
"filename": "disable_FastStartup.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "Disable Fast Startup",
"description": "Disables Faststartup on Windows 10",
"shell": "cmd"
},
{
"filename": "updatetacticalexclusion.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "TRMM Defender Exclusions",
"description": "Windows Defender Exclusions for Tactical RMM",
"shell": "powershell"
},
{
"filename": "Display_Message_To_User.ps1",
"submittedBy": "https://github.com/bradhawkins85",
"name": "Display Message To User",
"description": "Displays a popup message to the currently logged on user",
"shell": "powershell"
},
{
"filename": "VerifyAntivirus.ps1",
"submittedBy": "https://github.com/beejayzed",
"name": "Verify Antivirus Status",
"description": "Verify and display status for all installed Antiviruses",
"shell": "powershell"
},
{
"filename": "CreateAllUserLogonScript.ps1",
"submittedBy": "https://github.com/nr-plaxon",
"name": "Create User Logon Script",
"description": "Creates a powershell script that runs at logon of any user on the machine in the security context of the user.",
"shell": "powershell"
},
{
"filename": "Win_Chocolatey_Update_Installed.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Chocolatey Update Installed Apps",
"description": "Update all apps that were installed using Chocolatey.",
"shell": "cmd",
"category": "TRMM (Win):3rd Party Software>Chocolatey"
},
{
"filename": "Win_AD_Check_And_Enable_AD_Recycle_Bin.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "AD - Check and Enable AD Recycle Bin",
"description": "Only run on Domain Controllers, checks for Active Directory Recycle Bin and enables if not already enabled",
"shell": "powershell",
"category": "TRMM (Win):Active Directory"
},
{
"filename": "Check_Events_for_Bluescreens.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Check for Bluescreens",
"description": "This will check for Bluescreen events on your system",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"filename": "Win_Rename_Computer.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Rename Computer",
"description": "Rename computer. First parameter will be new PC name. 2nd parameter if yes will auto-reboot machine",
"shell": "powershell",
"category": "TRMM (Win):Other"
}
{
"guid": "6820cb5e-5a7f-4d9b-8c22-d54677e3cc04",
"filename": "Win_Clear_Firefox_Cache.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Firefox - Clean Cache",
"description": "This script will clean up Mozilla Firefox for all users.",
"shell": "powershell",
"category": "TRMM (Win):Browsers"
},
{
"guid": "3ff6a386-11d1-4f9d-8cca-1b0563bb6443",
"filename": "Win_Clear_Google_Chrome_Cache.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Chrome - Clear Cache for All Users",
"description": "This script will clean up Google Chrome for all users.",
"shell": "powershell",
"category": "TRMM (Win):Browsers"
},
{
"guid": "be1de837-f677-4ac5-aa0c-37a0fc9991fc",
"filename": "Win_Install_Adobe_Reader.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Adobe Reader DC - Install",
"description": "Installs Adobe Reader DC.",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software>Chocolatey"
},
{
"guid": "2ee134d5-76aa-4160-b334-a1efbc62079f",
"filename": "Win_Install_Duplicati.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Duplicati - Install",
"description": "This script installs Duplicati 2.0.5.1 as a service.",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software"
},
{
"guid": "81cc5bcb-01bf-4b0c-89b9-0ac0f3fe0c04",
"filename": "Win_Reset_Windows_Update.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Windows Update - Reset",
"description": "This script will reset all of the Windows Updates components to DEFAULT SETTINGS.",
"shell": "powershell",
"category": "TRMM (Win):Updates"
},
{
"guid": "8db87ff0-a9b4-4d9d-bc55-377bbcb85b6d",
"filename": "Win_Start_Cleanup.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Disk - Cleanup C: drive",
"description": "Cleans the C: drive's Window Temperary files, Windows SoftwareDistribution folder, the local users Temperary folder, IIS logs (if applicable) and empties the recycling bin. All deleted files will go into a log transcript in $env:TEMP. By default this script leaves files that are newer than 7 days old however this variable can be edited.",
"shell": "powershell",
"category": "TRMM (Win):Maintenance"
},
{
"guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3",
"filename": "Win_Defender_FullScan_Background.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Defender - Full Scan",
"description": "Runs a Windows Defender Full background scan.",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "adf81ddb-3b77-415c-a89b-2ccc826b5aa7",
"filename": "Win_Defender_QuickScan_Background.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Defender - Quick Scan",
"description": "Runs a Quick Scan using Windows Defender in the Background.",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "3c46290b-85db-4cd2-93a2-943c8c93b3b1",
"filename": "Speedtest.py",
"submittedBy": "https://github.com/wh1te909",
"name": "Speed Test - Python",
"description": "Runs a Speed Test using Python",
"shell": "python",
"category": "TRMM (Win):Network"
},
{
"guid": "9d34f482-1f0c-4b2f-b65f-a9cf3c13ef5f",
"filename": "Win_Rename_Installed_App.ps1",
"submittedBy": "https://github.com/bradhawkins85",
"name": "TacticalRMM Agent Rename",
"description": "Updates the DisplayName registry entry for the Tactical RMM windows agent to your desired name. This script takes 1 required argument: the name you wish to set.",
"shell": "powershell",
"category": "TRMM (Win):TacticalRMM Related"
},
{
"guid": "525ae965-1dcf-4c17-92b3-5da3cf6819f5",
"filename": "Win_Bitlocker_Encrypted_Drive_c.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Bitlocker - Check C Drive for Status",
"description": "Runs a check on drive C for Bitlocker status.",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "2ea35fa2-c227-4d17-a40e-4d39f252e27a",
"filename": "Win_Bitlocker_Create_Status_Report.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Bitlocker - Create Status Report",
"description": "Creates a Bitlocker status report.",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "9e5769c1-3873-4941-bf70-e851e0afbd6d",
"filename": "Win_Bitlocker_Retrieve_Status_Report.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Bitlocker - Retrieve Status Report",
"description": "Retreives a Bitlocker status report.",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "72b93487-0266-43f0-97cc-03d4c7ee0b44",
"filename": "Win_Bitlocker_Get_Recovery_Keys.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Bitlocker - Get Recovery Keys",
"description": "Retreives a Bitlocker Recovery Keys",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "cfa14c28-4dfc-4d4e-95ee-a380652e058d",
"filename": "Win_Bios_Check.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "BIOS - Check Information",
"description": "Retreives and reports on BIOS make, version, and date.",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "e1c27982-b955-4766-85b6-d92527a177cf",
"filename": "Win_Hardware_Monitor_Get_Info.ps1",
"submittedBy": "https://github.com/MaxAnderson95/",
"name": "Monitor - Get Info",
"description": "Retreives and reports on Monitor info: Manufacturer, Model, Serial",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "ae231ac4-b01f-4a39-a9d2-3d817af75260",
"filename": "Win_Hardware_RAM_Status.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "RAM - Check Information",
"description": "Retreives and reports on RAM info: DIMM's, total memory, slots total and used",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "95a2ee6f-b89b-4551-856e-3081b041caa7",
"filename": "Win_Reset_High_Performance_Power_Profile_to_Defaults.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Power Profile - Reset High Perf Power Profile to defaults",
"description": "Resets monitor, disk, standby, and hibernate timers in the default High Performance power profile to their default values. It also re-indexes the AC and DC power profiles into their default order.",
"shell": "powershell",
"category": "TRMM (Win):Power"
},
{
"guid": "2cbd30b0-84dd-4388-a36d-2e2e980f1a3e",
"filename": "Win_Set_High_Performance_Power_Profile.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Power Profile - Set High Performance",
"description": "Sets the High Performance Power profile to the active power profile. Use this to keep machines from falling asleep.",
"shell": "powershell",
"category": "TRMM (Win):Power"
},
{
"guid": "553236d3-81bc-49f4-af8a-0cff925a7f6d",
"filename": "Win_10_Upgrade.ps1",
"submittedBy": "https://github.com/RVL-Solutions and https://github.com/darimm",
"name": "Windows 10 Upgrade",
"description": "Forces an upgrade to the latest release of Windows 10.",
"shell": "powershell",
"category": "TRMM (Win):Updates"
},
{
"guid": "375323e5-cac6-4f35-a304-bb7cef35902d",
"filename": "Win_Disk_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Disk Hardware Health Check (using Event Viewer errors)",
"description": "Checks local disks for errors reported in event viewer within the last 24 hours",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "7c14beb4-d1c3-41aa-8e70-92a267d6e080",
"filename": "Win_Duplicati_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Duplicati - Check",
"description": "Checks Duplicati Backup is running properly over the last 24 hours",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software"
},
{
"guid": "da51111c-aff6-4d87-9d76-0608e1f67fe5",
"filename": "Win_Defender_Enable.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Defender - Enable",
"description": "Enables Windows Defender and sets preferences",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "a223d03a-e22e-40e0-94f2-92dd8c481d14",
"filename": "Win_Open_SSH_Server_Install.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "SSH - Install Feature and Enable",
"description": "Installs and enabled OpenSSH Server Feature in Win10",
"shell": "powershell",
"category": "TRMM (Win):Windows Features"
},
{
"guid": "2435297a-6263-4e90-8688-1847400d0e22",
"filename": "Win_RDP_enable.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "RDP - Enable",
"description": "Enables RDP",
"shell": "cmd",
"category": "TRMM (Win):Windows Features"
},
{
"guid": "24f19ead-fdfe-46b4-9dcb-4cd0e12a3940",
"filename": "Win_Speedtest.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Speed Test - Powershell",
"description": "Speed Test with Powershell(win 10 or server2016+)",
"shell": "powershell",
"category": "TRMM (Win):Network"
},
{
"guid": "a821975c-60df-4d58-8990-6cf8a55b4ee0",
"filename": "Win_Sync_Time.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "ADDC - Sync DC Time",
"description": "Syncs time with domain controller",
"shell": "cmd",
"category": "TRMM (Win):Active Directory"
},
{
"guid": "b6b9912f-4274-4162-99cc-9fd47fbcb292",
"filename": "Win_ADDC_Sync_Start.bat",
"submittedBy": "https://github.com/silversword411",
"name": "ADDC - Sync AD",
"description": "Trigger AD Sync on domain controller",
"shell": "cmd",
"category": "TRMM (Win):Active Directory"
},
{
"guid": "b720e320-7755-4c89-9992-e1a6c43699ed",
"filename": "Win_Defender_Clear_Logs.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Defender - Clear Logs",
"description": "Clears Windows Defender Logs",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "d980fda3-a068-47eb-8495-1aab07a24e64",
"filename": "Win_Defender_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Defender - Status",
"description": "This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "9956e936-6fdb-4488-a9d8-8b274658037f",
"filename": "Win_Disable_Fast_Startup.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "Power - Fast Startup Disable",
"description": "Disables Faststartup on Windows 10",
"shell": "cmd",
"category": "TRMM (Win):Power"
},
{
"guid": "f628a02b-16c3-4ab5-b788-dec5bc2af1d9",
"filename": "Win_Power_Disable_Hibernation.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Power - Hibernate Disable",
"description": "Disables Hibernation",
"shell": "cmd",
"category": "TRMM (Win):Power"
},
{
"guid": "2472bbaf-1941-4722-8a58-d1dd0f528801",
"filename": "Win_Update_Tactical_Exclusion.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "TRMM Defender Exclusions",
"description": "Windows Defender Exclusions for Tactical RMM",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "b253dc76-41a0-48ca-9cea-bee4277402c4",
"filename": "Win_Display_Message_To_User.ps1",
"submittedBy": "https://github.com/bradhawkins85",
"name": "Message Popup To User",
"description": "Displays a popup message to the currently logged on user",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "19224d21-bd39-44bc-b9cf-8f1ba3ca9c11",
"filename": "Win_Antivirus_Verify.ps1",
"submittedBy": "https://github.com/beejayzed",
"name": "Antivirus - Verify Status",
"description": "Verify and display status for all installed Antiviruses",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "f88c5c52-c6fe-44db-b727-b7912a4279ed",
"filename": "Win_Create_All_User_Logon_Script.ps1",
"submittedBy": "https://github.com/nr-plaxon",
"name": "Template Example - Create User Logon Script",
"description": "Creates a powershell script that runs at logon of any user on the machine in the security context of the user.",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "5615aa90-0272-427b-8acf-0ca019612501",
"filename": "Win_Chocolatey_Update_Installed.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Update Installed Apps",
"description": "Update all apps that were installed using Chocolatey.",
"shell": "cmd",
"category": "TRMM (Win):3rd Party Software>Chocolatey"
},
{
"guid": "fff8024d-d72e-4457-84fa-6c780f69a16f",
"filename": "Win_AD_Check_And_Enable_AD_Recycle_Bin.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "ADDC - Check and Enable AD Recycle Bin",
"description": "Only run on Domain Controllers, checks for Active Directory Recycle Bin and enables if not already enabled",
"shell": "powershell",
"category": "TRMM (Win):Active Directory"
},
{
"guid": "71090fc4-faa6-460b-adb0-95d7863544e1",
"filename": "Win_Check_Events_for_Bluescreens.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Bluescreen Notification",
"description": "Event Viewer Monitor - Notify Bluescreen events on your system",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "8373846f-facc-49b9-9891-3a780a394c89",
"filename": "Win_Local_User_Created_Monitor.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - New User Notification",
"description": "Event Viewer Monitor - Notify when new Local user is created",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "65e5cef1-8338-4180-a0bc-cd54e62de690",
"filename": "Win_Task_Scheduler_New_Items_Monitor.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Task Scheduler New Item Notification",
"description": "Event Viewer Monitor - Notify when new Task Scheduler item is created",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "08ca81f2-f044-4dfc-ad47-090b19b19d76",
"filename": "Win_User_Logged_in_with_Temp_Profile.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "User Logged in with temp profile check",
"description": "Check if users are logged in with a temp profile",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "5d905886-9eb1-4129-8b81-a013f842eb24",
"filename": "Win_Rename_Computer.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Rename Computer",
"description": "Rename computer. First parameter will be new PC name. 2nd parameter if yes will auto-reboot machine",
"shell": "powershell",
"category": "TRMM (Win):Other",
"default_timeout": 30
},
{
"guid": "f396dae2-c768-45c5-bd6c-176e56ed3614",
"filename": "Win_Finish_updates_and_restart.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Updates - Finish and restart",
"description": "Finish installing Windows updates and restart PC",
"shell": "powershell",
"category": "TRMM (Win):Updates"
},
{
"guid": "63f89be0-a9c9-4c61-9b55-bce0b28b90b2",
"filename": "Win_Finish_updates_and_shutdown.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Updates - Finish and Shutdown",
"description": "Finish installing Windows updates and shutdown PC",
"shell": "powershell",
"category": "TRMM (Win):Updates"
},
{
"guid": "e09895d5-ca13-44a2-a38c-6e77c740f0e8",
"filename": "Win_ScreenConnectAIO.ps1",
"submittedBy": "https://github.com/bradhawkins85",
"name": "ScreenConnect AIO",
"description": "Install, Uninstall, Start and Stop ScreenConnect Access Agent",
"args": [
"-serviceName {{client.ScreenConnectService}}",
"-url {{client.ScreenConnectInstaller}}",
"-action install"
],
"default_timeout": "90",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software"
},
{
"guid": "3abbb62a-3757-492c-8979-b4fc6174845d",
"filename": "Win_Disable_AutoRun.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Autorun - Disable",
"description": "Disable Autorun System Wide",
"shell": "cmd",
"category": "TRMM (Win):Other",
"default_timeout": "30"
},
{
"guid": "4a11877a-7555-494c-ac74-29d6df3c1989",
"filename": "Win_Disable_Cortana.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Cortana - Disable",
"description": "Disable Cortana System Wide",
"shell": "cmd",
"category": "TRMM (Win):Other",
"default_timeout": "30"
},
{
"guid": "28ef1387-dd4f-4bab-b042-26250914e370",
"filename": "Win_WOL_Enable_Status.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Network WoL - Enable function",
"description": "Wake on Lan enable on Dell, HP, Lenovo",
"shell": "powershell",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{
"guid": "685d5432-0b84-46d5-98e8-3ec2054150fe",
"filename": "Win_WOL_Test_State.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Network WoL - Test State",
"description": "Wake on Lan test status",
"shell": "powershell",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{
"guid": "abe78170-7cf9-435b-9666-c5ef6c11a106",
"filename": "Win_Network_IPv6_Disable.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Network IPv6 - Disable",
"description": "Disable IPv6 on all adapters",
"shell": "powershell",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{
"guid": "6ce5682a-49db-4c0b-9417-609cf905ac43",
"filename": "Win_Win10_Change_Key_and_Activate.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Product Key in Win10 Change and Activate",
"description": "Insert new product key and Activate. Requires 1 parameter the product key you want to use",
"shell": "powershell",
"category": "TRMM (Win):Other",
"default_timeout": "90"
},
{
"guid": "83f6c6ea-6120-4fd3-bec8-d3abc505dcdf",
"filename": "Win_TRMM_Start_Menu_Delete_Shortcut.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "TRMM Delete Start Menu Shortcut for App",
"description": "Tactical RMM delete its application shortcut that's installed in the start menu",
"shell": "powershell",
"category": "TRMM (Win):TacticalRMM Related",
"default_timeout": "10"
},
{
"guid": "60130fca-7636-446e-acd7-cc5d29d609c2",
"filename": "Win_Firewall_Check_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Windows Firewall - Check Status",
"description": "Windows Firewall - Check state, report status",
"shell": "powershell",
"category": "TRMM (Win):Network"
},
{
"guid": "93379675-c01c-433f-87df-a11597c959f0",
"filename": "Win_UAC_Check_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Windows UAC - Check Status",
"description": "Windows UAC - Report status",
"shell": "powershell",
"category": "TRMM (Win):Security"
},
{
"guid": "7ea6a11a-05c0-4151-b5c1-cb8af029299f",
"filename": "Win_AzureAD_Check_Connection_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Azure AD - Check Status",
"description": "Azure AD - Check if joined or not",
"shell": "powershell",
"category": "TRMM (Win):Azure>AD"
},
{
"guid": "7d81859a-1ba3-42b0-8664-29844f0dd765",
"filename": "Win_Azure_Mars_Cloud_Backup_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Azure - Mars Cloud backup Status",
"description": "Azure - Mars Cloud backup Check Status",
"shell": "powershell",
"category": "TRMM (Win):Azure>Backup"
},
{
"guid": "e18c64d0-b783-4b52-b44b-9bb7592b439b",
"filename": "Win_FileSystem_Enable_Long_Paths.bat",
"submittedBy": "https://github.com/silversword411",
"name": "File System - Enable Long Paths",
"description": "Enables NTFS Long paths greater than 260 characters",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "c6252ca8-5172-42ea-9114-e447f80868f5",
"filename": "Win_USB_Disable_Access.bat",
"submittedBy": "https://github.com/silversword411",
"name": "USB - Disable Access",
"description": "USB - Disable Plugged in USB devices",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "3785952f-69fb-4bda-b2fe-5e3e8642738a",
"filename": "Win_USB_Enable_Access.bat",
"submittedBy": "https://github.com/silversword411",
"name": "USB - Enable Access",
"description": "USB - Enable Plugged in USB devices",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "c6014da2-b188-4e1b-b96a-e3440ade3a6a",
"filename": "Win_RecycleBin_Empty.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "File System - Empty Recycle Bin",
"description": "Empty the recycle bin",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "57997ec7-b293-4fd5-9f90-a25426d0eb90",
"filename": "Win_Get_Computer_Users.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Get Computer Users",
"description": "Get list of computer users and show which one is enabled",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "77da9c87-5a7a-4ba1-bdde-3eeb3b01d62d",
"filename": "Win_Network_Set_To_Private.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Network Category - Set Network To Private",
"description": "Sets current network type to Private",
"shell": "powershell",
"category": "TRMM (Win):Network"
},
{
"guid": "768f42d5-7b45-45ed-8233-254ae537aaa2",
"filename": "Win_TaskScheduler_Add_Task.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Task Scheduler - Add a task",
"description": "Add a task to Task Scheduler, needs editing",
"shell": "powershell",
"category": "TRMM (Win):Other"
}
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 3.1.7 on 2021-04-01 14:52
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('scripts', '0006_script_default_timeout'),
]
operations = [
migrations.AddField(
model_name='script',
name='args',
field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(blank=True, null=True), blank=True, default=list, null=True, size=None),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-04-15 02:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('scripts', '0007_script_args'),
]
operations = [
migrations.AddField(
model_name='script',
name='guid',
field=models.CharField(blank=True, max_length=64, null=True),
),
]

View File

@@ -1,5 +1,9 @@
import base64
import re
from loguru import logger
from typing import Any, List, Union
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models
from logs.models import BaseAuditModel
@@ -15,8 +19,11 @@ SCRIPT_TYPES = [
("builtin", "Built In"),
]
logger.configure(**settings.LOG_CONFIG)
class Script(BaseAuditModel):
guid = name = models.CharField(max_length=64, null=True, blank=True)
name = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
filename = models.CharField(max_length=255) # deprecated
@@ -26,6 +33,12 @@ class Script(BaseAuditModel):
script_type = models.CharField(
max_length=100, choices=SCRIPT_TYPES, default="userdefined"
)
args = ArrayField(
models.TextField(null=True, blank=True),
null=True,
blank=True,
default=list,
)
favorite = models.BooleanField(default=False)
category = models.CharField(max_length=100, null=True, blank=True)
code_base64 = models.TextField(null=True, blank=True)
@@ -66,15 +79,28 @@ class Script(BaseAuditModel):
for script in info:
if os.path.exists(os.path.join(scripts_dir, script["filename"])):
s = cls.objects.filter(script_type="builtin").filter(
name=script["name"]
s = cls.objects.filter(script_type="builtin", guid=script["guid"])
category = (
script["category"] if "category" in script.keys() else "Community"
)
default_timeout = (
int(script["default_timeout"])
if "default_timeout" in script.keys()
else 90
)
args = script["args"] if "args" in script.keys() else []
if s.exists():
i = s.first()
i.name = script["name"]
i.description = script["description"]
i.category = "Community"
i.category = category
i.shell = script["shell"]
i.default_timeout = default_timeout
i.args = args
with open(os.path.join(scripts_dir, script["filename"]), "rb") as f:
script_bytes = (
@@ -87,10 +113,52 @@ class Script(BaseAuditModel):
"name",
"description",
"category",
"default_timeout",
"code_base64",
"shell",
"args",
]
)
# check if script was added without a guid
elif cls.objects.filter(
script_type="builtin", name=script["name"]
).exists():
s = cls.objects.get(script_type="builtin", name=script["name"])
if not s.guid:
print(f"Updating GUID for: {script['name']}")
s.guid = script["guid"]
s.name = script["name"]
s.description = script["description"]
s.category = category
s.shell = script["shell"]
s.default_timeout = default_timeout
s.args = args
with open(
os.path.join(scripts_dir, script["filename"]), "rb"
) as f:
script_bytes = (
f.read().decode("utf-8").encode("ascii", "ignore")
)
s.code_base64 = base64.b64encode(script_bytes).decode(
"ascii"
)
s.save(
update_fields=[
"guid",
"name",
"description",
"category",
"default_timeout",
"code_base64",
"shell",
"args",
]
)
else:
print(f"Adding new community script: {script['name']}")
@@ -102,17 +170,128 @@ class Script(BaseAuditModel):
cls(
code_base64=code_base64,
guid=script["guid"],
name=script["name"],
description=script["description"],
filename=script["filename"],
shell=script["shell"],
script_type="builtin",
category="Community",
category=category,
default_timeout=default_timeout,
args=args,
).save()
# delete community scripts that had their name changed
cls.objects.filter(script_type="builtin", guid=None).delete()
@staticmethod
def serialize(script):
# serializes the script and returns json
from .serializers import ScriptSerializer
return ScriptSerializer(script).data
@classmethod
def parse_script_args(
cls, agent, shell: str, args: List[str] = list()
) -> Union[List[str], None]:
from core.models import CustomField
if not list:
return []
temp_args = list()
# pattern to match for injection
pattern = re.compile(".*\\{\\{(.*)\\}\\}.*")
for arg in args:
match = pattern.match(arg)
if match:
# only get the match between the () in regex
string = match.group(1)
# split by period if exists. First should be model and second should be property
temp = string.split(".")
# check for model and property
if len(temp) != 2:
# ignore arg since it is invalid
continue
if temp[0] == "client":
model = "client"
obj = agent.client
elif temp[0] == "site":
model = "site"
obj = agent.site
elif temp[0] == "agent":
model = "agent"
obj = agent
else:
# ignore arg since it is invalid
continue
if hasattr(obj, temp[1]):
value = getattr(obj, temp[1])
elif CustomField.objects.filter(model=model, name=temp[1]).exists():
field = CustomField.objects.get(model=model, name=temp[1])
model_fields = getattr(field, f"{model}_fields")
value = None
if model_fields.filter(**{model: obj}).exists():
value = model_fields.get(**{model: obj}).value
if not value and field.default_value:
value = field.default_value
# check if value exists and if not use defa
if value and field.type == "multiple":
value = format_shell_array(shell, value)
elif value and field.type == "checkbox":
value = format_shell_bool(shell, value)
if not value:
continue
else:
# ignore arg since property is invalid
continue
# replace the value in the arg and push to array
# log any unhashable type errors
try:
temp_args.append(re.sub("\\{\\{.*\\}\\}", value, arg)) # type: ignore
except Exception as e:
logger.error(e)
continue
else:
temp_args.append(arg)
return temp_args
def format_shell_array(shell: str, value: Any) -> str:
if shell == "cmd":
return "array args are not supported with batch"
elif shell == "powershell":
temp_string = ""
for item in value:
temp_string += item + ","
return temp_string.strip(",")
else: # python
temp_string = ""
for item in value:
temp_string += item + ","
return temp_string.strip(",")
def format_shell_bool(shell: str, value: Any) -> str:
if shell == "cmd":
return "1" if value else "0"
elif shell == "powershell":
return "$True" if value else "$False"
else: # python
return "True" if value else "False"

View File

@@ -12,6 +12,7 @@ class ScriptTableSerializer(ModelSerializer):
"description",
"script_type",
"shell",
"args",
"category",
"favorite",
"default_timeout",
@@ -26,6 +27,7 @@ class ScriptSerializer(ModelSerializer):
"name",
"description",
"shell",
"args",
"category",
"favorite",
"code_base64",

View File

@@ -1,3 +1,4 @@
from email.policy import default
import json
import os
from pathlib import Path
@@ -23,7 +24,7 @@ class TestScriptViews(TacticalTestCase):
serializer = ScriptTableSerializer(scripts, many=True)
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEqual(serializer.data, resp.data)
self.assertEqual(serializer.data, resp.data) # type: ignore
self.check_not_authenticated("get", url)
@@ -37,10 +38,12 @@ class TestScriptViews(TacticalTestCase):
"category": "New",
"code": "Some Test Code\nnew Line",
"default_timeout": 99,
"args": ["hello", "world", r"{{agent.public_ip}}"],
"favorite": False,
}
# test without file upload
resp = self.client.post(url, data)
resp = self.client.post(url, data, format="json")
self.assertEqual(resp.status_code, 200)
self.assertTrue(Script.objects.filter(name="Name").exists())
self.assertEqual(Script.objects.get(name="Name").code, data["code"])
@@ -57,6 +60,9 @@ class TestScriptViews(TacticalTestCase):
"category": "New",
"filename": file,
"default_timeout": 4455,
"args": json.dumps(
["hello", "world", r"{{agent.public_ip}}"]
), # simulate javascript's JSON.stringify() for formData
}
# test with file upload
@@ -124,11 +130,11 @@ class TestScriptViews(TacticalTestCase):
self.assertEqual(resp.status_code, 404)
script = baker.make("scripts.Script")
url = f"/scripts/{script.pk}/script/"
url = f"/scripts/{script.pk}/script/" # type: ignore
serializer = ScriptSerializer(script)
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEqual(serializer.data, resp.data)
self.assertEqual(serializer.data, resp.data) # type: ignore
self.check_not_authenticated("get", url)
@@ -164,27 +170,27 @@ class TestScriptViews(TacticalTestCase):
script = baker.make(
"scripts.Script", code_base64="VGVzdA==", shell="powershell"
)
url = f"/scripts/{script.pk}/download/"
url = f"/scripts/{script.pk}/download/" # type: ignore
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data, {"filename": f"{script.name}.ps1", "code": "Test"})
self.assertEqual(resp.data, {"filename": f"{script.name}.ps1", "code": "Test"}) # type: ignore
# test batch file
script = baker.make("scripts.Script", code_base64="VGVzdA==", shell="cmd")
url = f"/scripts/{script.pk}/download/"
url = f"/scripts/{script.pk}/download/" # type: ignore
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data, {"filename": f"{script.name}.bat", "code": "Test"})
self.assertEqual(resp.data, {"filename": f"{script.name}.bat", "code": "Test"}) # type: ignore
# test python file
script = baker.make("scripts.Script", code_base64="VGVzdA==", shell="python")
url = f"/scripts/{script.pk}/download/"
url = f"/scripts/{script.pk}/download/" # type: ignore
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data, {"filename": f"{script.name}.py", "code": "Test"})
self.assertEqual(resp.data, {"filename": f"{script.name}.py", "code": "Test"}) # type: ignore
self.check_not_authenticated("get", url)
@@ -201,6 +207,7 @@ class TestScriptViews(TacticalTestCase):
) as f:
info = json.load(f)
guids = []
for script in info:
fn: str = script["filename"]
self.assertTrue(os.path.exists(os.path.join(scripts_dir, fn)))
@@ -217,6 +224,19 @@ class TestScriptViews(TacticalTestCase):
elif fn.endswith(".py"):
self.assertEqual(script["shell"], "python")
if "args" in script.keys():
self.assertIsInstance(script["args"], list)
# allows strings as long as they can be type casted to int
if "default_timeout" in script.keys():
self.assertIsInstance(int(script["default_timeout"]), int)
self.assertIn("guid", script.keys())
guids.append(script["guid"])
# check guids are unique
self.assertEqual(len(guids), len(set(guids)))
def test_load_community_scripts(self):
with open(
os.path.join(settings.BASE_DIR, "scripts/community_scripts.json")
@@ -225,9 +245,46 @@ class TestScriptViews(TacticalTestCase):
Script.load_community_scripts()
community_scripts = Script.objects.filter(script_type="builtin").count()
self.assertEqual(len(info), community_scripts)
community_scripts_count = Script.objects.filter(script_type="builtin").count()
if len(info) != community_scripts_count:
raise Exception(
f"There are {len(info)} scripts in json file but only {community_scripts_count} in database"
)
# test updating already added community scripts
Script.load_community_scripts()
self.assertEqual(len(info), community_scripts)
community_scripts_count2 = Script.objects.filter(script_type="builtin").count()
self.assertEqual(len(info), community_scripts_count2)
def test_community_script_has_jsonfile_entry(self):
with open(
os.path.join(settings.BASE_DIR, "scripts/community_scripts.json")
) as f:
info = json.load(f)
filenames = [i["filename"] for i in info]
# normal
if not settings.DOCKER_BUILD:
scripts_dir = os.path.join(Path(settings.BASE_DIR).parents[1], "scripts")
# docker
else:
scripts_dir = settings.SCRIPTS_DIR
with os.scandir(scripts_dir) as it:
for f in it:
if not f.name.startswith(".") and f.is_file():
if f.name not in filenames:
raise Exception(
f"{f.name} is missing an entry in community_scripts.json"
)
def test_script_filenames_do_not_contain_spaces(self):
with open(
os.path.join(settings.BASE_DIR, "scripts/community_scripts.json")
) as f:
info = json.load(f)
for script in info:
fn: str = script["filename"]
if " " in fn:
raise Exception(f"{fn} must not contain spaces in filename")

View File

@@ -1,4 +1,5 @@
import base64
import json
from django.conf import settings
from django.shortcuts import get_object_or_404
@@ -24,7 +25,6 @@ class GetAddScripts(APIView):
return Response(ScriptTableSerializer(scripts, many=True).data)
def post(self, request, format=None):
data = {
"name": request.data["name"],
"category": request.data["category"],
@@ -34,16 +34,24 @@ class GetAddScripts(APIView):
"script_type": "userdefined", # force all uploads to be userdefined. built in scripts cannot be edited by user
}
if "favorite" in request.data:
# code editor upload
if "args" in request.data.keys() and isinstance(request.data["args"], list):
data["args"] = request.data["args"]
# file upload, have to json load it cuz it's formData
if "args" in request.data.keys() and "file_upload" in request.data.keys():
data["args"] = json.loads(request.data["args"])
if "favorite" in request.data.keys():
data["favorite"] = request.data["favorite"]
if "filename" in request.data:
if "filename" in request.data.keys():
message_bytes = request.data["filename"].read()
data["code_base64"] = base64.b64encode(message_bytes).decode(
"ascii", "ignore"
)
elif "code" in request.data:
elif "code" in request.data.keys():
message_bytes = request.data["code"].encode("ascii", "ignore")
data["code_base64"] = base64.b64encode(message_bytes).decode("ascii")

View File

@@ -1,5 +1,6 @@
import json
import os
from unittest.mock import patch
from django.conf import settings
from model_bakery import baker
@@ -39,7 +40,7 @@ class TestSoftwareViews(TacticalTestCase):
# test without agent software
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEquals(resp.data, [])
self.assertEquals(resp.data, []) # type: ignore
# make some software
software = baker.make(
@@ -51,6 +52,70 @@ class TestSoftwareViews(TacticalTestCase):
serializer = InstalledSoftwareSerializer(software)
resp = self.client.get(url, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEquals(resp.data, serializer.data)
self.assertEquals(resp.data, serializer.data) # type: ignore
self.check_not_authenticated("get", url)
@patch("agents.models.Agent.nats_cmd")
def test_install(self, nats_cmd):
url = "/software/install/"
old_agent = baker.make_recipe("agents.online_agent", version="1.4.7")
data = {
"pk": old_agent.pk,
"name": "duplicati",
}
r = self.client.post(url, data, format="json")
self.assertEqual(r.status_code, 400)
agent = baker.make_recipe(
"agents.online_agent", version=settings.LATEST_AGENT_VER
)
data = {
"pk": agent.pk,
"name": "duplicati",
}
nats_cmd.return_value = "timeout"
r = self.client.post(url, data, format="json")
self.assertEqual(r.status_code, 400)
nats_cmd.reset_mock()
nats_cmd.return_value = "ok"
r = self.client.post(url, data, format="json")
self.assertEqual(r.status_code, 200)
self.check_not_authenticated("post", url)
@patch("agents.models.Agent.nats_cmd")
def test_refresh_installed(self, nats_cmd):
url = "/software/refresh/4827342/"
r = self.client.get(url, format="json")
self.assertEqual(r.status_code, 404)
nats_cmd.return_value = "timeout"
agent = baker.make_recipe("agents.agent")
baker.make(
"software.InstalledSoftware",
agent=agent,
software={},
)
url = f"/software/refresh/{agent.pk}/"
r = self.client.get(url, format="json")
self.assertEqual(r.status_code, 400)
with open(
os.path.join(settings.BASE_DIR, "tacticalrmm/test_data/software1.json")
) as f:
sw = json.load(f)
nats_cmd.reset_mock()
nats_cmd.return_value = sw
r = self.client.get(url, format="json")
self.assertEqual(r.status_code, 200)
s = agent.installedsoftware_set.first()
s.delete()
r = self.client.get(url, format="json")
self.assertEqual(r.status_code, 200)
self.check_not_authenticated("get", url)

View File

@@ -0,0 +1,20 @@
import os
import django
from channels.routing import ProtocolTypeRouter, URLRouter # isort:skip
from django.core.asgi import get_asgi_application # isort:skip
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tacticalrmm.settings")
django.setup()
from tacticalrmm.utils import KnoxAuthMiddlewareStack # isort:skip
from .urls import ws_urlpatterns # isort:skip
application = ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": KnoxAuthMiddlewareStack(URLRouter(ws_urlpatterns)),
}
)

View File

@@ -15,25 +15,32 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
AUTH_USER_MODEL = "accounts.User"
# latest release
TRMM_VERSION = "0.4.31"
TRMM_VERSION = "0.6.1"
# bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser
APP_VER = "0.0.124"
APP_VER = "0.0.130"
# https://github.com/wh1te909/rmmagent
LATEST_AGENT_VER = "1.4.13"
LATEST_AGENT_VER = "1.5.1"
MESH_VER = "0.7.93"
# for the update script, bump when need to recreate venv or npm install
PIP_VER = "13"
NPM_VER = "12"
PIP_VER = "15"
NPM_VER = "14"
DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}.exe"
DL_32 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}-x86.exe"
EXE_GEN_URL = "https://exe.tacticalrmm.io/api/v1/exe"
EXE_GEN_URLS = [
"https://exe2.tacticalrmm.io",
"https://exe.tacticalrmm.io",
]
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
ASGI_APPLICATION = "tacticalrmm.asgi.application"
try:
from .local_settings import *
@@ -45,6 +52,7 @@ INSTALLED_APPS = [
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.staticfiles",
"channels",
"rest_framework",
"rest_framework.authtoken",
"knox",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
import json
import os
from unittest.mock import mock_open, patch
import requests
from django.conf import settings
from django.test import TestCase, override_settings
from .utils import (
bitdays_to_string,
filter_software,
generate_winagent_exe,
get_bit_days,
reload_nats,
run_nats_api_cmd,
)
class TestUtils(TestCase):
@patch("requests.post")
@patch("__main__.__builtins__.open", new_callable=mock_open)
def test_generate_winagent_exe_success(self, m_open, mock_post):
r = generate_winagent_exe(
client=1,
site=1,
agent_type="server",
rdp=1,
ping=0,
power=0,
arch="64",
token="abc123",
api="https://api.example.com",
file_name="rmm-client-site-server.exe",
)
self.assertEqual(r.status_code, 200)
@patch("requests.post")
def test_generate_winagent_exe_timeout(self, mock_post):
mock_post.side_effect = requests.exceptions.ConnectionError()
r = generate_winagent_exe(
client=1,
site=1,
agent_type="server",
rdp=1,
ping=0,
power=0,
arch="64",
token="abc123",
api="https://api.example.com",
file_name="rmm-client-site-server.exe",
)
self.assertEqual(r.status_code, 400)
@override_settings(
CERT_FILE="/tmp/asdasd.pem",
KEY_FILE="/tmp/asds55r.pem",
ALLOWED_HOSTS=["api.example.com"],
SECRET_KEY="sekret",
DOCKER_BUILD=True,
)
@patch("subprocess.run")
def test_reload_nats_docker(self, mock_subprocess):
_ = reload_nats()
mock_subprocess.assert_not_called()
@override_settings(
ALLOWED_HOSTS=["api.example.com"],
SECRET_KEY="sekret",
)
@patch("subprocess.run")
def test_reload_nats(self, mock_subprocess):
_ = reload_nats()
mock_subprocess.assert_called_once()
@patch("subprocess.run")
def test_run_nats_api_cmd(self, mock_subprocess):
ids = ["a", "b", "c"]
_ = run_nats_api_cmd("monitor", ids)
mock_subprocess.assert_called_once()
def test_bitdays_to_string(self):
a = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
all_days = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
]
bit_weekdays = get_bit_days(a)
r = bitdays_to_string(bit_weekdays)
self.assertEqual(r, ", ".join(a))
bit_weekdays = get_bit_days(all_days)
r = bitdays_to_string(bit_weekdays)
self.assertEqual(r, "Every day")
def test_filter_software(self):
with open(
os.path.join(settings.BASE_DIR, "tacticalrmm/test_data/software1.json")
) as f:
sw = json.load(f)
r = filter_software(sw)
self.assertIsInstance(r, list)

View File

@@ -3,6 +3,7 @@ from django.urls import include, path
from knox import views as knox_views
from accounts.views import CheckCreds, LoginView
from core import consumers
urlpatterns = [
path("checkcreds/", CheckCreds.as_view()),
@@ -29,3 +30,7 @@ if hasattr(settings, "ADMIN_ENABLED") and settings.ADMIN_ENABLED:
from django.contrib import admin
urlpatterns += (path(settings.ADMIN_URL, admin.site.urls),)
ws_urlpatterns = [
path("ws/dashinfo/", consumers.DashInfo.as_asgi()), # type: ignore
]

View File

@@ -2,15 +2,25 @@ import json
import os
import string
import subprocess
import tempfile
import time
import urllib.parse
from typing import Union
import pytz
import requests
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.http import FileResponse
from knox.auth import TokenAuthentication
from loguru import logger
from rest_framework import status
from rest_framework.response import Response
from agents.models import Agent
from core.models import CodeSignToken
logger.configure(**settings.LOG_CONFIG)
@@ -29,6 +39,87 @@ WEEK_DAYS = {
}
def generate_winagent_exe(
client: int,
site: int,
agent_type: str,
rdp: int,
ping: int,
power: int,
arch: str,
token: str,
api: str,
file_name: str,
) -> Union[Response, FileResponse]:
from agents.utils import get_exegen_url
inno = (
f"winagent-v{settings.LATEST_AGENT_VER}.exe"
if arch == "64"
else f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe"
)
try:
codetoken = CodeSignToken.objects.first().token
base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {
"version": settings.LATEST_AGENT_VER,
"arch": arch,
"token": codetoken,
}
dl_url = base_url + urllib.parse.urlencode(params)
except:
codetoken = ""
dl_url = settings.DL_64 if arch == "64" else settings.DL_32
data = {
"client": client,
"site": site,
"agenttype": agent_type,
"rdp": str(rdp),
"ping": str(ping),
"power": str(power),
"goarch": "amd64" if arch == "64" else "386",
"token": token,
"inno": inno,
"url": dl_url,
"api": api,
"codesigntoken": codetoken,
}
headers = {"Content-type": "application/json"}
errors = []
with tempfile.NamedTemporaryFile() as fp:
for url in settings.EXE_GEN_URLS:
try:
r = requests.post(
f"{url}/api/v1/exe",
json=data,
headers=headers,
stream=True,
timeout=900,
)
except Exception as e:
errors.append(str(e))
else:
errors = []
break
if errors:
logger.error(errors)
return notify_error(
"Something went wrong. Check debug error log for exact error message"
)
with open(fp.name, "wb") as f:
for chunk in r.iter_content(chunk_size=1024): # type: ignore
if chunk:
f.write(chunk)
del r
return FileResponse(open(fp.name, "rb"), as_attachment=True, filename=file_name)
def get_default_timezone():
from core.models import CoreSettings
@@ -38,7 +129,7 @@ def get_default_timezone():
def get_bit_days(days: list[str]) -> int:
bit_days = 0
for day in days:
bit_days |= WEEK_DAYS.get(day)
bit_days |= WEEK_DAYS.get(day) # type: ignore
return bit_days
@@ -128,3 +219,47 @@ def reload_nats():
subprocess.run(
["/usr/local/bin/nats-server", "-signal", "reload"], capture_output=True
)
@database_sync_to_async
def get_user(access_token):
try:
auth = TokenAuthentication()
token = access_token.decode().split("access_token=")[1]
user = auth.authenticate_credentials(token.encode())
except Exception:
return AnonymousUser()
else:
return user[0]
class KnoxAuthMiddlewareInstance:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
scope["user"] = await get_user(scope["query_string"])
return await self.app(scope, receive, send)
KnoxAuthMiddlewareStack = lambda inner: KnoxAuthMiddlewareInstance(
AuthMiddlewareStack(inner)
)
def run_nats_api_cmd(mode: str, ids: list[str], timeout: int = 30) -> None:
config = {
"key": settings.SECRET_KEY,
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
"agents": ids,
}
with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, "w") as f:
json.dump(config, f)
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", mode]
try:
subprocess.run(cmd, capture_output=True, timeout=timeout)
except Exception as e:
logger.error(e)

View File

@@ -14,8 +14,29 @@ class TestWinUpdateViews(TacticalTestCase):
self.authenticate()
self.setup_coresettings()
def test_get_winupdates(self):
@patch("agents.models.Agent.nats_cmd")
def test_run_update_scan(self, nats_cmd):
agent = baker.make_recipe("agents.agent")
url = f"/winupdate/{agent.pk}/runupdatescan/"
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
nats_cmd.assert_called_with({"func": "getwinupdates"}, wait=False)
self.check_not_authenticated("get", url)
@patch("agents.models.Agent.nats_cmd")
def test_install_updates(self, nats_cmd):
agent = baker.make_recipe("agents.agent")
baker.make("winupdate.WinUpdate", agent=agent, _quantity=4)
baker.make("winupdate.WinUpdatePolicy", agent=agent)
url = f"/winupdate/{agent.pk}/installnow/"
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
nats_cmd.assert_called_once()
self.check_not_authenticated("get", url)
def test_get_winupdates(self):
agent = baker.make_recipe("agents.agent")
baker.make("winupdate.WinUpdate", agent=agent, _quantity=4)
@@ -27,8 +48,8 @@ class TestWinUpdateViews(TacticalTestCase):
resp = self.client.get(url, format="json")
serializer = UpdateSerializer(agent)
self.assertEqual(resp.status_code, 200)
self.assertEqual(len(resp.data["winupdates"]), 4)
self.assertEqual(resp.data, serializer.data)
self.assertEqual(len(resp.data["winupdates"]), 4) # type: ignore
self.assertEqual(resp.data, serializer.data) # type: ignore
self.check_not_authenticated("get", url)
@@ -99,7 +120,7 @@ class TestWinUpdateViews(TacticalTestCase):
resp = self.client.patch(url, invalid_data, format="json")
self.assertEqual(resp.status_code, 404)
data = {"pk": winupdate.pk, "policy": "inherit"}
data = {"pk": winupdate.pk, "policy": "inherit"} # type: ignore
resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200)

View File

@@ -1,12 +1,11 @@
import asyncio
from django.shortcuts import get_object_or_404
from packaging import version as pyver
from rest_framework.decorators import api_view
from rest_framework.response import Response
from agents.models import Agent
from tacticalrmm.utils import get_default_timezone, notify_error
from tacticalrmm.utils import get_default_timezone
from .models import WinUpdate
from .serializers import UpdateSerializer
@@ -24,9 +23,6 @@ def get_win_updates(request, pk):
def run_update_scan(request, pk):
agent = get_object_or_404(Agent, pk=pk)
agent.delete_superseded_updates()
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
return notify_error("Requires agent version 1.3.0 or greater")
asyncio.run(agent.nats_cmd({"func": "getwinupdates"}, wait=False))
return Response("ok")
@@ -35,9 +31,6 @@ def run_update_scan(request, pk):
def install_updates(request, pk):
agent = get_object_or_404(Agent, pk=pk)
agent.delete_superseded_updates()
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
return notify_error("Requires agent version 1.3.0 or greater")
agent.approve_updates()
nats_data = {
"func": "installwinupdates",

View File

@@ -27,7 +27,7 @@ jobs:
source env/bin/activate
cd /myagent/_work/1/s/api/tacticalrmm
pip install --no-cache-dir --upgrade pip
pip install --no-cache-dir setuptools==53.0.0 wheel==0.36.2
pip install --no-cache-dir setuptools==54.2.0 wheel==0.36.2
pip install --no-cache-dir -r requirements.txt -r requirements-test.txt -r requirements-dev.txt
displayName: "Install Python Dependencies"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
SCRIPT_VERSION="11"
SCRIPT_VERSION="12"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh'
GREEN='\033[0;32m'
@@ -73,6 +73,9 @@ sudo tar -czvf ${tmp_dir}/nginx/etc-nginx.tar.gz -C /etc/nginx .
sudo tar -czvf ${tmp_dir}/confd/etc-confd.tar.gz -C /etc/conf.d .
sudo cp ${sysd}/rmm.service ${sysd}/celery.service ${sysd}/celerybeat.service ${sysd}/meshcentral.service ${sysd}/nats.service ${tmp_dir}/systemd/
if [ -f "${sysd}/daphne.service" ]; then
sudo cp ${sysd}/daphne.service ${tmp_dir}/systemd/
fi
cat /rmm/api/tacticalrmm/tacticalrmm/private/log/debug.log | gzip -9 > ${tmp_dir}/rmm/debug.log.gz
cp /rmm/api/tacticalrmm/tacticalrmm/local_settings.py /rmm/api/tacticalrmm/app.ini ${tmp_dir}/rmm/

View File

@@ -3,7 +3,7 @@ FROM node:14-alpine AS builder
WORKDIR /home/node/app
COPY ./web/package.json .
RUN npm install -g npm
RUN npm install -g npm@latest
RUN npm install
COPY ./web .

View File

@@ -2,6 +2,14 @@
set -e
: "${DEV:=0}"
if [ "${DEV}" = 1 ]; then
NATS_CONFIG=/workspace/api/tacticalrmm/nats-rmm.conf
else
NATS_CONFIG="${TACTICAL_DIR}/api/nats-rmm.conf"
fi
sleep 15
until [ -f "${TACTICAL_READY_FILE}" ]; do
echo "waiting for init container to finish install or update..."

View File

@@ -63,8 +63,19 @@ server {
alias ${TACTICAL_DIR}/api/tacticalrmm/private/;
}
location ~ ^/(natsapi) {
deny all;
location ~ ^/ws/ {
set \$api http://tactical-websockets:8383;
proxy_pass \$api;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host \$server_name;
}
error_log /var/log/nginx/api-error.log;

View File

@@ -62,4 +62,4 @@ ENTRYPOINT ["/entrypoint.sh"]
WORKDIR ${TACTICAL_DIR}/api
EXPOSE 80
EXPOSE 80 443 8383

View File

@@ -29,14 +29,15 @@ function check_tactical_ready {
# tactical-init
if [ "$1" = 'tactical-init' ]; then
mkdir -p ${TACTICAL_DIR}/tmp
mkdir -p ${TACTICAL_DIR}/scripts/userdefined
test -f "${TACTICAL_READY_FILE}" && rm "${TACTICAL_READY_FILE}"
# copy container data to volume
rsync -a --no-perms --no-owner --delete --exclude "tmp/*" --exclude "certs/*" --exclude="api/tacticalrmm/private/*" "${TACTICAL_TMP_DIR}/" "${TACTICAL_DIR}/"
mkdir -p ${TACTICAL_DIR}/tmp
mkdir -p ${TACTICAL_DIR}/api/tacticalrmm/private/exe
mkdir -p ${TACTICAL_DIR}/api/tacticalrmm/logs
until (echo > /dev/tcp/"${POSTGRES_HOST}"/"${POSTGRES_PORT}") &> /dev/null; do
echo "waiting for postgresql container to be ready..."
sleep 5
@@ -62,6 +63,9 @@ DOCKER_BUILD = True
CERT_FILE = '/opt/tactical/certs/fullchain.pem'
KEY_FILE = '/opt/tactical/certs/privkey.pem'
EXE_DIR = '/opt/tactical/api/tacticalrmm/private/exe'
LOG_DIR = '/opt/tactical/api/tacticalrmm/private/log'
SCRIPTS_DIR = '/opt/tactical/scripts'
ALLOWED_HOSTS = ['${API_HOST}', 'tactical-backend']
@@ -164,3 +168,12 @@ if [ "$1" = 'tactical-celerybeat' ]; then
test -f "${TACTICAL_DIR}/api/celerybeat.pid" && rm "${TACTICAL_DIR}/api/celerybeat.pid"
celery -A tacticalrmm beat -l info
fi
# backend container
if [ "$1" = 'tactical-websockets' ]; then
check_tactical_ready
export DJANGO_SETTINGS_MODULE=tacticalrmm.settings
daphne tacticalrmm.asgi:application --port 8383 -b 0.0.0.0
fi

View File

@@ -22,6 +22,7 @@ volumes:
services:
# postgres database for api service
tactical-postgres:
container_name: trmm-postgres
image: postgres:13-alpine
restart: always
environment:
@@ -35,6 +36,7 @@ services:
# redis container for celery tasks
tactical-redis:
container_name: trmm-redis
image: redis:6.0-alpine
restart: always
networks:
@@ -42,6 +44,7 @@ services:
# used to initialize the docker environment
tactical-init:
container_name: trmm-init
image: ${IMAGE_REPO}tactical:${VERSION}
restart: on-failure
command: ["tactical-init"]
@@ -65,6 +68,7 @@ services:
# nats
tactical-nats:
container_name: trmm-nats
image: ${IMAGE_REPO}tactical-nats:${VERSION}
restart: always
environment:
@@ -80,6 +84,7 @@ services:
# meshcentral container
tactical-meshcentral:
container_name: trmm-meshcentral
image: ${IMAGE_REPO}tactical-meshcentral:${VERSION}
restart: always
environment:
@@ -101,6 +106,7 @@ services:
# mongodb container for meshcentral
tactical-mongodb:
container_name: trmm-mongodb
image: mongo:4.4
restart: always
environment:
@@ -114,6 +120,7 @@ services:
# container that hosts vue frontend
tactical-frontend:
container_name: trmm-frontend
image: ${IMAGE_REPO}tactical-frontend:${VERSION}
restart: always
networks:
@@ -123,6 +130,7 @@ services:
# container for django backend
tactical-backend:
container_name: trmm-backend
image: ${IMAGE_REPO}tactical:${VERSION}
command: ["tactical-backend"]
restart: always
@@ -135,8 +143,25 @@ services:
depends_on:
- tactical-postgres
tactical-nginx:
# container for django websockets connections
tactical-websockets:
container_name: trmm-websockets
image: ${IMAGE_REPO}tactical:${VERSION}
command: ["tactical-websockets"]
restart: always
networks:
- proxy
- api-db
- redis
volumes:
- tactical_data:/opt/tactical
depends_on:
- tactical-postgres
- tactical-backend
# container for tactical reverse proxy
tactical-nginx:
container_name: trmm-nginx
image: ${IMAGE_REPO}tactical-nginx:${VERSION}
restart: always
environment:
@@ -156,6 +181,7 @@ services:
# container for celery worker service
tactical-celery:
container_name: trmm-celery
image: ${IMAGE_REPO}tactical:${VERSION}
command: ["tactical-celery"]
restart: always
@@ -171,6 +197,7 @@ services:
# container for celery beat service
tactical-celerybeat:
container_name: trmm-celerybeat
image: ${IMAGE_REPO}tactical:${VERSION}
command: ["tactical-celerybeat"]
restart: always

View File

@@ -4,7 +4,7 @@ A backup script is provided for quick and easy way to backup all settings into o
Download the backup script:
```bash
wget https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh
wget -N https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh
```
From the Web UI, click **Tools > Server Maintenance**

View File

@@ -0,0 +1,104 @@
Hidden docs, needs work
For local Hyper-v Devbox notes
From https://raw.githubusercontent.com/silversword411/tacticalrmm-devdocs
Needs an official install_devbox.sh script
# Setup local devbox in hyper-v VM
## Install Ubuntu 20.04 LTS
Don't forget to
```
sudo apt-get updates && sudo apt-get upgrade
```
### Optional
Set all users in sudo group not to require password every time:
```
sudo visudo
```
Add this:
```
%sudo ALL=(ALL) NOPASSWD: ALL
```
## Download customized install script and tweak
Create folder to dump into
```
sudo mkdir /rmm
sudo chown ${USER}:${USER} -R /rmm
cd /rmm
```
Get dev install script
```
wget https://raw.githubusercontent.com/silversword411/tacticalrmm-devdocs/blob/main/install_devbox.sh
```
Edit, and search for `REPLACEMEWITHYOURFORKEDREPOURL`
and replace with your forked repo URL (example commented out below)
## Run it
```
./install_devbox.sh
```
## Watch for
![Image](images/installcomplete.png)
!!!Note Unlike regular installs, don't worry about the QR code
## Celebrate
![Image](images/celebrate.gif)
# Misc commands
### Start mkdocs on dev box
```bash
cd /rmm/api
source env/bin/activate
pip install --upgrade pip
pip install --upgrade setuptools wheel
pip install -r tacticalrmm/requirements-dev.txt
cd /rmm/docs
mkdocs serve
```
### Running tests locally
Prep and update
```bash
source /rmm/api/env/bin/activate
cd /rmm/api/tacticalrmm
pip install -r requirements.txt
```
Then run tests
```
python manage.py test
```

View File

@@ -0,0 +1,97 @@
### 1. Install vscode
[https://code.visualstudio.com/download](https://code.visualstudio.com/download)
### 2. Fork Project in Github
This is making a duplicate of the code under your Github that you can edit
[https://github.com/wh1te909/tacticalrmm](https://github.com/wh1te909/tacticalrmm)
![ForkIt](images/vscode-forkit.png)
### 3. Add your (forked) repo to vscode
Clone repository
Login to your Github
Choose local folder
#### 3a. Install extra vscode Extensions
GitLens
Remote - SSH
### 4. Open Terminal
[https://code.visualstudio.com/docs/editor/integrated-terminal](https://code.visualstudio.com/docs/editor/integrated-terminal)
```
Ctrl+`
```
### 5. Configure a remote for your fork (in vscode)
[https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork)
Configure your local fork and tell it where the original code repo is so you can compare and merge updates later when official repo is updated
Check repos
```
git remote -v
```
Add upstream repo
```
git remote add upstream https://github.com/wh1te909/tacticalrmm
```
Confirm changes
```
git remote -v
```
### 6. Contribute code
Make changes to something.
`Commit` (update something) and notate what you did
`Push` (from your local vscode to your github fork)
Open browser and look at your repo (It should reflect your commit)
#### 6a. Request your changes to be pulled into the primary repo (Pull Request)
![Changes you've made need integration with master repo](images/trmm_contribute-notice.png)
In browser create pull request
### 7. Sync your local fork
[https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork)
Bring changes from original repo to your local vscode copy so you're current with changes made in original Github repo
![Sync Fork](images/trmm_need_sync_local_fork.png)
```
git pull --rebase upstream develop
```
#### 7a. Push your local updated copy to your Github fork
Then you're `push`ing that updated local repo to your online Github fork
![Sync push/pulls](images/trmm_vscode_git_pending.png)
### 8. Verify and Repeat
Check your Github fork in browser, should be up to date now with original. Repeat 6 or 7 as necessary

View File

@@ -0,0 +1,74 @@
example of `/etc/nginx/sites-available/rmm.conf`
**DO NOT COPY PASTE INTO YOUR SERVER ONLY USE AS A REFERENCE**
```
server_tokens off;
upstream tacticalrmm {
server unix:////rmm/api/tacticalrmm/tacticalrmm.sock;
}
map $http_user_agent $ignore_ua {
"~python-requests.*" 0;
"~go-resty.*" 0;
default 1;
}
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name api.example.com;
client_max_body_size 300M;
access_log /rmm/api/tacticalrmm/tacticalrmm/private/log/access.log combined if=$ignore_ua;
error_log /rmm/api/tacticalrmm/tacticalrmm/private/log/error.log;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
location /static/ {
root /rmm/api/tacticalrmm;
}
location /private/ {
internal;
add_header "Access-Control-Allow-Origin" "https://rmm.example.com";
alias /rmm/api/tacticalrmm/tacticalrmm/private/;
}
location ~ ^/(natsapi) {
allow 127.0.0.1;
deny all;
uwsgi_pass tacticalrmm;
include /etc/nginx/uwsgi_params;
uwsgi_read_timeout 500s;
uwsgi_ignore_client_abort on;
}
location ~ ^/ws/ {
proxy_pass http://unix:/rmm/daphne.sock;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location / {
uwsgi_pass tacticalrmm;
include /etc/nginx/uwsgi_params;
uwsgi_read_timeout 9999s;
uwsgi_ignore_client_abort on;
}
}
```

View File

@@ -0,0 +1,16 @@
# Custom Fields
**Settings > Global Settings > Custom Fields**
v0.5.0 adds support for custom fields to be used in scripts.
It also exposes some pre-defined fields that are already in the database.
Please check the following video for examples until proper docs are written:
[https://www.youtube.com/watch?v=0-5jGGL3FOM](https://www.youtube.com/watch?v=0-5jGGL3FOM)

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@@ -43,11 +43,6 @@ python manage.py get_mesh_exe_url
#### Bulk update agent offline/overdue time
```bash
wget -q https://raw.githubusercontent.com/wh1te909/tacticalrmm/develop/api/tacticalrmm/agents/management/commands/bulk_change_checkin.py -O /rmm/api/tacticalrmm/agents/management/commands/bulk_change_checkin.py
```
Examples:
Change offline time on all agents to 5 minutes
```bash
python manage.py bulk_change_checkin --offline --all 5

View File

@@ -53,7 +53,7 @@ Please then copy/paste the logs and post them either in our [Discord support cha
<br/>
#### Web UI frozen or not loading / website errors / general errors
#### All other errors
First, run the [update script](update_server.md#updating-to-the-latest-rmm-version) with the `--force` flag. <br/>This will fix permissions and reinstall python/node packages that might have gotten corrupted.
@@ -61,10 +61,15 @@ First, run the [update script](update_server.md#updating-to-the-latest-rmm-versi
./update.sh --force
```
Check the debug log from the web UI: **File > Debug Log**
Open your browser's dev tools (ctrl + shift + j on chrome) and check the Console tab for any errors
Check all the systemd services that the rmm uses to function and check to make sure they're all active/running and enabled:
```bash
sudo systemctl status rmm
sudo systemctl status daphne
sudo systemctl status celery
sudo systemctl status celerybeat
sudo systemctl status nginx

View File

@@ -4,8 +4,6 @@
You should periodically run `sudo apt update` and `sudo apt -y upgrade` to keep your server up to date.
You can also update `npm` if prompted to by a message that might appear when running the `update.sh` script.
Other than this, you should avoid making any changes to your server and let the `update.sh` script handle everything else for you.
#### Updating to the latest RMM version

View File

@@ -11,6 +11,7 @@ nav:
- "Updating the RMM (Docker)": update_docker.md
- "Updating Agents": update_agents.md
- Functionality:
- "Custom Fields": functions/custom_fields.md
- "Remote Background": functions/remote_bg.md
- "Maintenance Mode": functions/maintenance_mode.md
- "Alerting": alerting.md
@@ -22,7 +23,9 @@ nav:
- FAQ: faq.md
- Management Commands: management_cmds.md
- MeshCentral Integration: mesh_integration.md
- Contributing: contributing.md
- Contributing:
- "Contributing to Docs": contributing.md
- "Contributing using VSCode": contributing_using_vscode.md
- License: license.md
site_description: "A remote monitoring and management tool"
site_author: "wh1te909"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
SCRIPT_VERSION="44"
SCRIPT_VERSION="46"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
sudo apt install -y curl wget dirmngr gnupg lsb-release
@@ -132,31 +132,14 @@ CHECK_HOSTS=$(grep 127.0.1.1 /etc/hosts | grep "$rmmdomain" | grep "$meshdomain"
HAS_11=$(grep 127.0.1.1 /etc/hosts)
if ! [[ $CHECK_HOSTS ]]; then
echo -ne "${GREEN}We need to append your 3 subdomains to the line starting with 127.0.1.1 in your hosts file.${NC}\n"
until [[ $edithosts =~ (y|n) ]]; do
echo -ne "${GREEN}Would you like me to do this for you? [y/n]${NC}: "
read edithosts
done
if [[ $edithosts == "y" ]]; then
if [[ $HAS_11 ]]; then
sudo sed -i "/127.0.1.1/s/$/ ${rmmdomain} $frontenddomain $meshdomain/" /etc/hosts
else
echo "127.0.1.1 ${rmmdomain} $frontenddomain $meshdomain" | sudo tee --append /etc/hosts > /dev/null
fi
else
if [[ $HAS_11 ]]; then
echo -ne "${GREEN}Please manually edit your /etc/hosts file to match the line below and re-run this script.${NC}\n"
sed "/127.0.1.1/s/$/ ${rmmdomain} $frontenddomain $meshdomain/" /etc/hosts | grep 127.0.1.1
else
echo -ne "\n${GREEN}Append the following line to your /etc/hosts file${NC}\n"
echo "127.0.1.1 ${rmmdomain} $frontenddomain $meshdomain"
fi
exit 1
fi
print_green 'Adding subdomains to hosts file'
if [[ $HAS_11 ]]; then
sudo sed -i "/127.0.1.1/s/$/ ${rmmdomain} ${frontenddomain} ${meshdomain}/" /etc/hosts
else
echo "127.0.1.1 ${rmmdomain} ${frontenddomain} ${meshdomain}" | sudo tee --append /etc/hosts > /dev/null
fi
fi
BEHIND_NAT=false
IPV4=$(ip -4 addr | sed -ne 's|^.* inet \([^/]*\)/.* scope global.*$|\1|p' | head -1)
if echo "$IPV4" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192\.168)'; then
@@ -376,7 +359,7 @@ python3.9 -m venv env
source /rmm/api/env/bin/activate
cd /rmm/api/tacticalrmm
pip install --no-cache-dir --upgrade pip
pip install --no-cache-dir setuptools==53.0.0 wheel==0.36.2
pip install --no-cache-dir setuptools==54.2.0 wheel==0.36.2
pip install --no-cache-dir -r /rmm/api/tacticalrmm/requirements.txt
python manage.py migrate
python manage.py collectstatic --no-input
@@ -444,6 +427,26 @@ EOF
)"
echo "${rmmservice}" | sudo tee /etc/systemd/system/rmm.service > /dev/null
daphneservice="$(cat << EOF
[Unit]
Description=django channels daemon
After=network.target
[Service]
User=${USER}
Group=www-data
WorkingDirectory=/rmm/api/tacticalrmm
Environment="PATH=/rmm/api/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/rmm/api/env/bin/daphne -u /rmm/daphne.sock tacticalrmm.asgi:application
Restart=always
RestartSec=3s
[Install]
WantedBy=multi-user.target
EOF
)"
echo "${daphneservice}" | sudo tee /etc/systemd/system/daphne.service > /dev/null
natsservice="$(cat << EOF
[Unit]
Description=NATS Server
@@ -514,6 +517,20 @@ server {
uwsgi_ignore_client_abort on;
}
location ~ ^/ws/ {
proxy_pass http://unix:/rmm/daphne.sock;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host \$server_name;
}
location / {
uwsgi_pass tacticalrmm;
include /etc/nginx/uwsgi_params;
@@ -713,7 +730,7 @@ sudo ln -s /etc/nginx/sites-available/frontend.conf /etc/nginx/sites-enabled/fro
print_green 'Enabling Services'
for i in rmm.service celery.service celerybeat.service nginx
for i in rmm.service daphne.service celery.service celerybeat.service nginx
do
sudo systemctl enable ${i}
sudo systemctl stop ${i}
@@ -783,7 +800,7 @@ sudo systemctl start nats.service
sed -i 's/ADMIN_ENABLED = True/ADMIN_ENABLED = False/g' /rmm/api/tacticalrmm/tacticalrmm/local_settings.py
print_green 'Restarting services'
for i in rmm.service celery.service celerybeat.service
for i in rmm.service daphne.service celery.service celerybeat.service
do
sudo systemctl stop ${i}
sudo systemctl start ${i}

View File

@@ -1,6 +1,6 @@
#!/bin/bash
SCRIPT_VERSION="22"
SCRIPT_VERSION="24"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
sudo apt update
@@ -291,8 +291,9 @@ python3.9 -m venv env
source /rmm/api/env/bin/activate
cd /rmm/api/tacticalrmm
pip install --no-cache-dir --upgrade pip
pip install --no-cache-dir setuptools==53.0.0 wheel==0.36.2
pip install --no-cache-dir setuptools==54.2.0 wheel==0.36.2
pip install --no-cache-dir -r /rmm/api/tacticalrmm/requirements.txt
python manage.py migrate
python manage.py collectstatic --no-input
python manage.py reload_nats
deactivate
@@ -323,7 +324,7 @@ sudo chown -R $USER:$GROUP /home/${USER}/.cache
print_green 'Enabling Services'
sudo systemctl daemon-reload
for i in celery.service celerybeat.service rmm.service nginx
for i in celery.service celerybeat.service rmm.service daphne.service nginx
do
sudo systemctl enable ${i}
sudo systemctl stop ${i}

View File

@@ -1,21 +0,0 @@
# Checks local disks for errors reported in event viewer within the last 24 hours
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='system';ID='11','9','15','52','129','7','98';Level=2,3;ProviderName='*disk*','*storsvc*','*ntfs*';StartTime=$TimeSpan})
{
Write-Output "Disk errors detected please investigate"
Get-WinEvent -FilterHashtable @{LogName='system';ID='11','9','15','52','129','7','98';Level=2,3;ProviderName='*disk*','*storsvc*','*ntfs*';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "Disks are Healthy"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,2 @@
net start ADSync
exit

View File

@@ -0,0 +1,15 @@
$ErrorActionPreference = 'silentlycontinue'
$aadchk = dsregcmd /status | Where-Object { $_ -match 'AzureAdJoined : ' } | ForEach-Object { $_.Trim() }
if ($aadchk -Eq 'AzureAdJoined : Yes') {
Write-Output "Machine is Azure Ad Joined"
exit 0
}
else {
Write-Output "Machine is not Azure Ad Joined"
exit 1
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,23 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
##Check for Errors in Backup
if (Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='11','18';StartTime=$TimeSpan})
{
Write-Host "Cloud Backup Mars Ended with Errors"
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','11','18','16';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Host "Cloud Backup Mars Backup Is Working Correctly"
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','16'}
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
manage-bde -protectors C: -get

View File

@@ -13,8 +13,8 @@ exit 1
{
else
Write-Output "No bluescreen events detected"
Write-Output "No bluescreen events detected in the past 24 hours."
exit 0
}
Exit $LASTEXITCODE
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" /v NoDriveTypeAutoRun /t REG_DWORD /d 255 /f

View File

@@ -0,0 +1 @@
reg add "hklm\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /d "AllowCortana"=dword:00000000

View File

@@ -0,0 +1,19 @@
# Checks local disks for errors reported in event viewer within the last 24 hours
$ErrorActionPreference = 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName = 'system'; ID = '11', '9', '15', '52', '129', '7', '98'; Level = 2, 3; ProviderName = '*disk*', '*storsvc*', '*ntfs*'; StartTime = $TimeSpan } -MaxEvents 10 | Where-Object -Property Message -Match Volume*)
{
Write-Output "Disk errors detected please investigate"
Get-WinEvent -FilterHashtable @{LogName = 'system'; ID = '11', '9', '15', '52', '129', '7', '98'; Level = 2, 3; ProviderName = '*disk*', '*storsvc*', '*ntfs*'; StartTime = $TimeSpan }
exit 1
}
else {
Write-Output "Disks are Healthy"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /V LongPathsEnabled /T REG_DWORD /D 1 /F

View File

@@ -0,0 +1,5 @@
# This script will force the computer to finish installing updates
# and restart. Normal restart doesn't install updates before issuing
# a reboot command. This one does.
Restart-Computer -ComputerName $env:COMPUTERNAME -Force

View File

@@ -0,0 +1,5 @@
# This script will force the computer to finish installing updates
# and shutdown. Normal shutdown doesn't install updates before issuing
# a shutdown command. This one does.
Stop-Computer -ComputerName $env:COMPUTERNAME -Force

View File

@@ -0,0 +1,17 @@
$ErrorActionPreference = 'silentlycontinue'
$fwenabled = (get-netfirewallprofile -policystore activestore).Enabled
if ($fwenabled.Contains('True')) {
Write-Output "Firewall is Enabled"
netsh advfirewall show currentprofile
exit 0
}
else {
Write-Host "Firewall is Disabled"
exit 1
}
Exit $LASTEXITCODE

Some files were not shown because too many files have changed in this diff Show More