├── .coveragerc
├── .editorconfig
├── .flake8
├── .gitignore
├── .gitmodules
├── .idea
├── ProbeManager.iml
├── codeStyles
│ └── Project.xml
├── deployment.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── .travis.yml
├── CHANGELOG.rst
├── CODE_OF_CONDUCT.md
├── CONTRIBUTORS.txt
├── LICENSE
├── README.rst
├── docs
├── Makefile
├── _static
│ └── .init
├── changelog.rst
├── conf.py
├── data
│ ├── Deployement_example_of_Probemanager_in_a_VPS.png
│ ├── Deployement_example_of_Probemanager_in_a_network.png
│ └── favicon.ico
├── index.rst
├── internal
│ ├── core.rst
│ ├── index.rst
│ └── rules.rst
├── modules
│ └── .init
└── readme.rst
├── install.sh
├── merge-develop.sh
├── probemanager
├── LICENSE
├── README.rst
├── api
│ ├── __init__.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── pagination.py
│ ├── serializers.py
│ ├── urls.py
│ └── views.py
├── core
│ ├── README.rst
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── data
│ │ ├── admin-index.png
│ │ ├── admin-jobs-full.png
│ │ ├── admin-jobs.png
│ │ ├── admin-server-add.png
│ │ └── admin-sshkey-add.png
│ ├── exceptions.py
│ ├── fixtures
│ │ ├── test-core-probe.json
│ │ ├── test-core-probeconfiguration.json
│ │ └── test-core-sshkey.json
│ ├── forms.py
│ ├── git.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── modelsmixins.py
│ ├── notifications.py
│ ├── ssh.py
│ ├── static
│ │ └── core
│ │ │ ├── bootstrap
│ │ │ ├── css
│ │ │ │ ├── bootstrap-grid.min.css
│ │ │ │ ├── bootstrap-reboot.min.css
│ │ │ │ └── bootstrap.min.css
│ │ │ └── js
│ │ │ │ └── bootstrap.min.js
│ │ │ ├── css
│ │ │ └── style.css
│ │ │ ├── images
│ │ │ └── favicon.ico
│ │ │ └── js
│ │ │ ├── jquery-3.2.1.min.js
│ │ │ ├── popper.min.js
│ │ │ └── reload.js
│ ├── tasks.py
│ ├── templates
│ │ └── core
│ │ │ ├── base.html
│ │ │ ├── index.html
│ │ │ └── index_probe.html
│ ├── templatetags
│ │ ├── __init__.py
│ │ ├── status.py
│ │ └── version.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_api.py
│ │ ├── test_models.py
│ │ ├── test_ssh.py
│ │ ├── test_utils.py
│ │ └── test_views.py
│ ├── urls.py
│ ├── utils.py
│ └── views.py
├── manage.py
├── probemanager
│ ├── __init__.py
│ ├── celery.py
│ ├── fixtures
│ │ ├── crontab.json
│ │ └── init.json
│ ├── settings
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── dev.py
│ │ └── prod.py
│ ├── urls.py
│ └── wsgi.py
├── rules
│ ├── README.rst
│ ├── __init__.py
│ ├── apps.py
│ ├── fixtures
│ │ ├── test-rules-rule.json
│ │ ├── test-rules-ruleset.json
│ │ └── test-rules-source.json
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── templates
│ │ └── rules
│ │ │ └── search.html
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_models.py
│ │ └── test_views.py
│ ├── urls.py
│ └── views.py
├── runtests.py
├── scripts
│ ├── __init__.py
│ ├── apache.py
│ ├── db_password.py
│ ├── generate_doc.py
│ ├── remove_in_file.py
│ ├── secrets.py
│ ├── setup_smtp.py
│ ├── setup_tests.py
│ ├── utilities.py
│ └── version.py
└── templates
│ └── admin
│ ├── base.html
│ ├── base_site.html
│ └── index.html
├── requirements
├── base.txt
├── dev.txt
├── os
│ ├── debian.txt
│ ├── debian_prod.txt
│ ├── osx.txt
│ └── osx_prod.txt
├── prod.txt
└── test.txt
├── secrets.tar.enc
├── start.sh
├── stop_celery.sh
└── test.sh
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | plugins =
3 | django_coverage_plugin
4 | [report]
5 | omit = */tests/*
6 | venv/*
7 | /home/travis/virtualenv/*
8 | */apps.py
9 | probemanager/probemanager/celery.py
10 | probemanager/probemanager/settings/*
11 | probemanager/manage.py
12 | probemanager/runtests.py
13 | */migrations/*
14 | */__init__.py
15 | */settings.py
16 | suricata-*/*
17 | [html]
18 | title = "ProbeManager Coverage report"
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 |
9 | [*.{py,rst,ini}]
10 | indent_size = 4
11 | indent_style = space
12 |
13 | [*.{html,css,scss,json,yml}]
14 | indent_size = 2
15 | indent_style = space
16 |
17 | [*.md]
18 | trim_trailing_whitespace = false
19 |
--------------------------------------------------------------------------------
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 120
3 | exclude =
4 | .git,
5 | __pycache__,
6 | docs/,
7 | venv/,
8 | htmlcov/,
9 | manage.py,
10 | suricata-*/*,
11 | */migrations/*,
12 | */tests/*,
13 | probemanager/probemanager/settings/*,
14 | max-complexity = 30
15 | #E501 line too long
16 | #E402 Not importing files at top of the file
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Python template
3 | # Byte-compiled / optimized / DLL files
4 | __pycache__/
5 | *.py[cod]
6 | *$py.class
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | env/
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib64/
21 | parts/
22 | sdist/
23 | var/
24 | wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *,cover
48 | .hypothesis/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 |
58 | # Flask stuff:
59 | instance/
60 | .webassets-cache
61 |
62 | # Scrapy stuff:
63 | .scrapy
64 |
65 | # Sphinx documentation
66 | docs/_build/
67 |
68 | # PyBuilder
69 | target/
70 |
71 | # Jupyter Notebook
72 | .ipynb_checkpoints
73 |
74 | # pyenv
75 | .python-version
76 |
77 | # celery beat schedule file
78 | celerybeat-schedule
79 |
80 | # SageMath parsed files
81 | *.sage.py
82 |
83 | # dotenv
84 | .env
85 |
86 | # virtualenv
87 | .venv
88 | venv/
89 | ENV/
90 |
91 | # Spyder project settings
92 | .spyderproject
93 |
94 | # Rope project settings
95 | .ropeproject
96 |
97 | ### JetBrains template
98 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
99 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
100 |
101 | # User-specific stuff:
102 | .idea/**/workspace.xml
103 | .idea/**/tasks.xml
104 | .idea/dictionaries
105 |
106 | # Sensitive or high-churn files:
107 | .idea/**/dataSources/
108 | .idea/**/dataSources.ids
109 | .idea/**/dataSources.xml
110 | .idea/**/dataSources.local.xml
111 | .idea/**/sqlDataSources.xml
112 | .idea/**/dynamic.xml
113 | .idea/**/uiDesigner.xml
114 | .idea/dbnavigator.xml
115 |
116 | # Gradle:
117 | .idea/**/gradle.xml
118 | .idea/**/libraries
119 |
120 | # CMake
121 | cmake-build-debug/
122 | cmake-build-release/
123 |
124 | # Mongo Explorer plugin:
125 | .idea/**/mongoSettings.xml
126 |
127 | ## File-based project format:
128 | *.iws
129 |
130 | ## Plugin-specific files:
131 |
132 | # IntelliJ
133 | out/
134 |
135 | # mpeltonen/sbt-idea plugin
136 | .idea_modules/
137 |
138 | # JIRA plugin
139 | atlassian-ide-plugin.xml
140 |
141 | # Cursive Clojure plugin
142 | .idea/replstate.xml
143 |
144 | # Crashlytics plugin (for Android Studio and IntelliJ)
145 | com_crashlytics_export_strings.xml
146 | crashlytics.properties
147 | crashlytics-build.properties
148 | fabric.properties
149 |
150 |
151 | ### Specific Project
152 | probemanager.db
153 | probemanager/celery.pid
154 | probemanager/celerybeat-schedule.db
155 | geckodriver.log
156 | migrations/*
157 | probemanager/bro/migrations/0*
158 | core/migrations/0*
159 | probemanager/rules/migrations/0*
160 | probemanager/Probe_Manager.egg-info
161 | probemanager/build
162 | probemanager/static/
163 | /probemanager/probemanager/fixtures/test-suricata-probe.json
164 | /probemanager/ssh_keys/
165 | /probemanager/pcap_success/
166 | /conf.ini
167 | /progress.json
168 | /probemanager/tmp/
169 | /docs/modules/*.rst
170 | /probemanager/version.txt
171 | .coveralls.yml
172 | /core/fixtures/init-test.json
173 | /probemanager/core/version.txt
174 | /probemanager/core/migrations/0*
175 | /probemanager/core/fixtures/test-core-secrets.json
176 | /probemanager/core/fixtures/test-core-secrets.ini
177 | secrets.tar
178 | /probemanager/file_test_success/
179 | /erl_crash.dump
180 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "suricata"]
2 | path = probemanager/suricata
3 | url = https://github.com/treussart/ProbeManager_Suricata.git
4 | branch = master
5 | [submodule "bro"]
6 | path = probemanager/bro
7 | url = https://github.com/treussart/ProbeManager_Bro.git
8 | branch = master
9 | [submodule "ossec"]
10 | path = probemanager/ossec
11 | url = https://github.com/treussart/ProbeManager_Ossec.git
12 | branch = master
13 | [submodule "checkcve"]
14 | path = probemanager/checkcve
15 | url = https://github.com/treussart/ProbeManager_CheckCVE.git
16 | branch = master
17 |
--------------------------------------------------------------------------------
/.idea/ProbeManager.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/.idea/deployment.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | notifications:
2 | email:
3 | recipients:
4 | - matthieu@treussart.com
5 | on_success: change
6 | on_failure: always
7 | language: python
8 | python:
9 | - '3.5'
10 | - '3.6'
11 | env:
12 | global:
13 | - MOZ_HEADLESS=1
14 | - SURICATA_VERSION="4.0.4"
15 | - BRO_VERSION="2.5.3"
16 | - OSSEC_VERSION="2.9.3"
17 | os:
18 | - linux
19 | sudo: required
20 | branches:
21 | only:
22 | - master
23 | - develop
24 | services:
25 | - postgresql
26 | - rabbitmq
27 | install:
28 | - "./install.sh prod ."
29 | script:
30 | - "./test.sh"
31 | before_install:
32 | - mkdir probemanager/ssh_keys
33 | - openssl aes-256-cbc -K $encrypted_0112b2c0954f_key -iv $encrypted_0112b2c0954f_iv
34 | -in secrets.tar.enc -out secrets.tar -d
35 | - tar xvf secrets.tar
36 | before_script:
37 | - export DISPLAY=:99.0
38 | - sh -e /etc/init.d/xvfb start
39 | - sleep 3
40 | addons:
41 | firefox: latest
42 | deploy:
43 | provider: pages
44 | local-dir: docs/_build/html
45 | skip-cleanup: true
46 | github-token: "$GITHUB_TOKEN"
47 | keep-history: false
48 | target-branch: gh-pages
49 | on:
50 | branch: master
51 |
--------------------------------------------------------------------------------
/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | =========
2 | Changelog
3 | =========
4 |
5 | All notable changes to this project will be documented in this file.
6 |
7 | [Unreleased]
8 | ------------
9 |
10 | [1.2.0] - 2018-04-30
11 | --------------------
12 |
13 | Added
14 | ~~~~~
15 |
16 | - Add Classtype to Suricata.
17 |
18 | Changed
19 | ~~~~~~~
20 |
21 | - Improved API.
22 | - Improved Documentation.
23 |
24 | Removed
25 | ~~~~~~~
26 |
27 | - Remove Classtype from rules
28 |
29 |
30 | [1.1.0] - 2018-04-19
31 | --------------------
32 |
33 | Added
34 | ~~~~~
35 |
36 | - Bro IDS.
37 | - Import rules from MISP in Suricata
38 |
39 | Changed
40 | ~~~~~~~
41 |
42 | - Refactoring sources
43 |
44 | Removed
45 | ~~~~~~~
46 |
47 |
48 | [1.0.0] - 2018-04-06
49 | --------------------
50 |
51 | Added
52 | ~~~~~
53 |
54 | - Suricata IDS.
55 | - CheckCVE.
56 |
57 | Changed
58 | ~~~~~~~
59 |
60 | - Increase Tests
61 |
62 | Removed
63 | ~~~~~~~
64 |
65 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at matthieu@treussart.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.txt:
--------------------------------------------------------------------------------
1 | Treussart Matthieu
2 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ############
2 | ProbeManager
3 | ############
4 |
5 | .. image:: https://www.ko-fi.com/img/donate_sm.png
6 | :alt: Donate
7 | :target: https://ko-fi.com/mtreussart
8 |
9 | |Licence| |Version|
10 |
11 |
12 | .. image:: https://api.codacy.com/project/badge/Grade/afc2ab5226584ac3b594eb09ebcc2ccc?branch=master
13 | :alt: Codacy Grade
14 | :target: https://app.codacy.com/app/treussart/ProbeManager?utm_source=github.com&utm_medium=referral&utm_content=treussart/ProbeManager&utm_campaign=badger
15 |
16 | .. image:: https://api.codacy.com/project/badge/Coverage/8c16c475964d4db58ce0c7de0d03abbf?branch=master
17 | :alt: Codacy Coverage
18 | :target: https://www.codacy.com/app/treussart/ProbeManager?utm_source=github.com&utm_medium=referral&utm_content=treussart/ProbeManager&utm_campaign=Badge_Coverage
19 |
20 | +------------------+--------------------+
21 | | Status | Operating system |
22 | +==================+====================+
23 | | |Build_Status| | Linux x86\_64 |
24 | +------------------+--------------------+
25 |
26 | .. |Licence| image:: https://img.shields.io/github/license/treussart/ProbeManager.svg
27 | .. |Stars| image:: https://img.shields.io/github/stars/treussart/ProbeManager.svg
28 | .. |Forks| image:: https://img.shields.io/github/forks/treussart/ProbeManager.svg
29 | .. |Downloads| image:: https://img.shields.io/github/downloads/treussart/ProbeManager/total.svg
30 | .. |Version| image:: https://img.shields.io/github/tag/treussart/ProbeManager.svg
31 | .. |Commits| image:: https://img.shields.io/github/commits-since/treussart/ProbeManager/latest.svg
32 | .. |Build_Status| image:: https://travis-ci.org/treussart/ProbeManager.svg?branch=master
33 | :target: https://travis-ci.org/treussart/ProbeManager
34 |
35 | Presentation
36 | ============
37 |
38 | It is common to see that many IDS (intrusion and detection system), including the software and its rules are not updated regularly. This can be explained by the fact the software and rule management is often complicated, which can be a particular problem for small and medium sized enterprises that normally lack system security expertise and full time operators to supervise their respective IDS. This finding encouraged me to develop an application (ProbeManager) that will better manage network and machine detection probes on a system.
39 |
40 | ProbeManager is an application that centralizes the management of intrusion detection systems. The purpose of ProbeManager is to simplify the deployment of detection probes and to put together all of their functionalities in one single place. ProbeManager also allows you to check the status of the probes and to be notified whenever there is a problem or dysfunction. ProbeManager is not a SIEM (security information and event management), therefore, it doesn’t display the probe outputs (alerts, logs, etc…)
41 |
42 | ProbeManager is currently compatible with NIDS Suricata and Bro, and it will soon also be compatible with OSSEC.
43 |
44 | Features
45 | --------
46 |
47 | * Search rules in all probes.
48 | * List installed probes and their status (Running or not, uptime ...).
49 | * Install, update probe.
50 | * Start, stop, reload and restart probe.
51 | * Push, Email notifications (change of status, ...).
52 | * API Restfull.
53 | * See all asynchronous jobs.
54 |
55 | Usage
56 | =====
57 |
58 | .. image:: https://raw.githubusercontent.com/treussart/ProbeManager/master/docs/data/Deployement_example_of_Probemanager_in_a_network.png
59 | :alt: Deployement example of Probemanager in a network
60 |
61 | .. image:: https://raw.githubusercontent.com/treussart/ProbeManager/master/docs/data/Deployement_example_of_Probemanager_in_a_VPS.png
62 | :alt: Deployement example of Probemanager in a VPS
63 |
64 | Installation
65 | ============
66 |
67 | Operating System
68 | ----------------
69 |
70 | +------------+------------+-----------+
71 | | OS | prod | test |
72 | +============+============+===========+
73 | | OSX 12+ | | X |
74 | +------------+------------+-----------+
75 | | Debian 9 | X | |
76 | +------------+------------+-----------+
77 | | Ubuntu 14 | X | |
78 | +------------+------------+-----------+
79 |
80 | OSX 12+ (Only for project development), Debian stable and Ubuntu 14.04+ are Supported and tested.
81 |
82 | Requirements
83 | ------------
84 |
85 | - Python3.5+
86 | - Pip
87 | - Rabbitmq-server (installed with install script)
88 | - Postgresql (installed with install script)
89 |
90 | Retrieve the project
91 | --------------------
92 |
93 | `Source code on Github `_
94 |
95 | .. code-block:: sh
96 |
97 | git clone --recursive https://github.com/treussart/ProbeManager.git
98 |
99 | Install
100 | -------
101 |
102 | For developer :
103 | ^^^^^^^^^^^^^^^
104 |
105 | .. code-block:: sh
106 |
107 | ./install.sh
108 | ./start.sh
109 |
110 | For Production :
111 | ^^^^^^^^^^^^^^^^
112 |
113 | Default destination path : /usr/local/share
114 |
115 | For same destination path : .
116 |
117 | Be sure to have the write rights in the destination path.
118 |
119 | .. code-block:: sh
120 |
121 | ./install.sh prod [destination path]
122 |
123 | With Django server (not recommended) :
124 |
125 | .. code-block:: sh
126 |
127 | [destination path]./start.sh prod
128 |
129 | With Apache (Only for Debian) :
130 |
131 | .. code-block:: sh
132 |
133 | http://localhost
134 |
135 | Launch the tests
136 | ----------------
137 |
138 | (Only for Dev or Travis) :
139 |
140 | .. code-block:: sh
141 |
142 | ./test.sh
143 |
144 |
145 | Open the file with a web browser :
146 |
147 | ::
148 |
149 | coverage_html/index.html
150 |
151 |
152 | Add a submodule
153 | ===============
154 |
155 | .. code-block:: sh
156 |
157 | git submodule add -b master --name suricata https://github.com/treussart/ProbeManager_Suricata.git probemanager/suricata
158 |
159 | Modules must respect a few rules:
160 |
161 | * A file version.txt (generated by install script)
162 | * A file README.rst
163 | * A folder api with a variable 'urls_to_register' into urls.py (Optional)
164 | * An install script : install.sh (Optional)
165 | * A script for initializing the database : init_db.sh (Optional)
166 |
167 |
168 | Documentation
169 | =============
170 |
171 |
172 | Respect standard : reStructuredText (RST).
173 |
174 | .. code-block:: sh
175 |
176 | venv/bin/python probemanager/manage.py runscript generate_doc --settings=probemanager.settings.dev
177 |
178 |
179 | Open the file with a web browser :
180 |
181 | ::
182 |
183 | docs/_build/html/index.html
184 |
185 | Or retrieve the full documentation `here `_
186 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SPHINXPROJ = probemanager
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/_static/.init:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/docs/_static/.init
--------------------------------------------------------------------------------
/docs/changelog.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../CHANGELOG.rst
2 |
3 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import re
4 | import datetime
5 |
6 |
7 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
8 | import django
9 | django.setup()
10 | from django.conf import settings
11 |
12 |
13 | def get_infos():
14 | filename = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) + \
15 | '/probemanager/probemanager/__init__.py'
16 | with open(filename) as fh:
17 | metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", fh.read()))
18 | return metadata
19 |
20 |
21 | # -- General configuration ------------------------------------------------
22 |
23 | last_reviewed = datetime.date.today().strftime('%d, %b %Y')
24 | rst_epilog = '.. |last_reviewed| replace:: %s' % last_reviewed
25 |
26 |
27 | # If your documentation needs a minimal Sphinx version, state it here.
28 | #
29 | # needs_sphinx = '1.0'
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = ['sphinx.ext.autodoc',
35 | 'sphinx.ext.todo',
36 | 'sphinx.ext.viewcode',
37 | 'sphinx.ext.extlinks',
38 | 'sphinx.ext.autosummary']
39 |
40 | # Add any paths that contain templates here, relative to this directory.
41 | templates_path = ['_templates']
42 |
43 | # The suffix(es) of source filenames.
44 | # You can specify multiple suffix as a list of string:
45 | #
46 | # source_suffix = ['.rst', '.md']
47 | source_suffix = '.rst'
48 |
49 | # The master toctree document.
50 | master_doc = 'index'
51 |
52 | # General information about the project.
53 | project = get_infos()['title']
54 | copyright = get_infos()['licence']
55 | author = get_infos()['author']
56 |
57 | # The version info for the project you're documenting, acts as replacement for
58 | # |version| and |release|, also used in various other places throughout the
59 | # built documents.
60 | #
61 | # The short X.Y version.
62 | version = settings.VERSION
63 |
64 | # The full version, including alpha/beta/rc tags.
65 | release = version
66 |
67 | # The language for content autogenerated by Sphinx. Refer to documentation
68 | # for a list of supported languages.
69 | #
70 | # This is also used if you do content translation via gettext catalogs.
71 | # Usually you set "language" from the command line for these cases.
72 | language = None
73 |
74 | # List of patterns, relative to source directory, that match files and
75 | # directories to ignore when looking for source files.
76 | # This patterns also effect to html_static_path and html_extra_path
77 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
78 |
79 | # The name of the Pygments (syntax highlighting) style to use.
80 | pygments_style = 'sphinx'
81 |
82 | # If true, `todo` and `todoList` produce output, else they produce nothing.
83 | todo_include_todos = True
84 |
85 |
86 | # -- Options for HTML output ----------------------------------------------
87 |
88 | # The theme to use for HTML and HTML Help pages. See the documentation for
89 | # a list of builtin themes.
90 | #
91 | html_theme = 'sphinx_rtd_theme'
92 |
93 | html_theme_path = ["_themes", ]
94 |
95 | # Theme options are theme-specific and customize the look and feel of a theme
96 | # further. For a list of options available for each theme, see the
97 | # documentation.
98 | #
99 | # html_theme_options = {}
100 |
101 | # Add any paths that contain custom static files (such as style sheets) here,
102 | # relative to this directory. They are copied after the builtin static files,
103 | # so a file named "default.css" will overwrite the builtin "default.css".
104 | html_static_path = ['_static']
105 | html_favicon = 'data/favicon.ico'
106 |
107 | # -- Options for HTMLHelp output ------------------------------------------
108 |
109 | # Output file base name for HTML help builder.
110 | htmlhelp_basename = 'probemanagerdoc'
111 |
112 | # -- Options for LaTeX output ---------------------------------------------
113 |
114 | latex_elements = {
115 | # The paper size ('letterpaper' or 'a4paper').
116 | #
117 | # 'papersize': 'letterpaper',
118 |
119 | # The font size ('10pt', '11pt' or '12pt').
120 | #
121 | # 'pointsize': '10pt',
122 |
123 | # Additional stuff for the LaTeX preamble.
124 | #
125 | # 'preamble': '',
126 |
127 | # Latex figure (float) alignment
128 | #
129 | # 'figure_align': 'htbp',
130 | }
131 |
132 | # Grouping the document tree into LaTeX files. List of tuples
133 | # (source start file, target name, title,
134 | # author, documentclass [howto, manual, or own class]).
135 | latex_documents = [
136 | (master_doc, 'probemanager.tex', u'Probe Manager Documentation',
137 | u'BSD', 'manual'),
138 | ]
139 |
140 |
141 | # -- Options for manual page output ---------------------------------------
142 |
143 | # One entry per manual page. List of tuples
144 | # (source start file, name, description, authors, manual section).
145 | man_pages = [
146 | (master_doc, 'probemanager', u'Probe Manager Documentation',
147 | [author], 1)
148 | ]
149 |
150 |
151 | # -- Options for Texinfo output -------------------------------------------
152 |
153 | # Grouping the document tree into Texinfo files. List of tuples
154 | # (source start file, target name, title, author,
155 | # dir menu entry, description, category)
156 | texinfo_documents = [
157 | (master_doc, 'probemanager', u'Probe Manager Documentation',
158 | author, 'probemanager', 'One line description of project.',
159 | 'Miscellaneous'),
160 | ]
161 |
162 | html_theme_options = {
163 | "collapse_navigation": True
164 | }
165 |
166 | suppress_warnings = ['image.nonlocal_uri']
167 |
168 | # Warn about all references where the target cannot be found
169 | nitpicky = False
170 |
--------------------------------------------------------------------------------
/docs/data/Deployement_example_of_Probemanager_in_a_VPS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/docs/data/Deployement_example_of_Probemanager_in_a_VPS.png
--------------------------------------------------------------------------------
/docs/data/Deployement_example_of_Probemanager_in_a_network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/docs/data/Deployement_example_of_Probemanager_in_a_network.png
--------------------------------------------------------------------------------
/docs/data/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/docs/data/favicon.ico
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 |
2 | Welcome to ProbeManager's documentation!
3 | ========================================
4 |
5 | :Last Rewiewed: |last_reviewed|
6 |
7 |
8 | .. toctree::
9 | :maxdepth: 2
10 | :caption: ProbeManager
11 |
12 | readme
13 | changelog
14 |
15 | .. toctree::
16 | :maxdepth: 2
17 | :caption: Internals modules
18 |
19 | internal/index
20 |
21 | .. toctree::
22 | :maxdepth: 2
23 | :caption: Modules
24 |
25 | modules/index
26 |
27 |
28 | Indices and tables
29 | ==================
30 |
31 | * :ref:`genindex`
32 | * :ref:`modindex`
33 | * :ref:`search`
34 |
--------------------------------------------------------------------------------
/docs/internal/core.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../../probemanager/core/README.rst
2 |
--------------------------------------------------------------------------------
/docs/internal/index.rst:
--------------------------------------------------------------------------------
1 |
2 |
3 | Rules
4 | -----
5 |
6 | .. toctree::
7 | :maxdepth: 2
8 |
9 | rules
10 |
11 | Core
12 | ----
13 |
14 | .. toctree::
15 | :maxdepth: 2
16 |
17 | core
18 |
--------------------------------------------------------------------------------
/docs/internal/rules.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../../probemanager/rules/README.rst
2 |
--------------------------------------------------------------------------------
/docs/modules/.init:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/docs/modules/.init
--------------------------------------------------------------------------------
/docs/readme.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../README.rst
--------------------------------------------------------------------------------
/merge-develop.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | git submodule foreach git checkout master
4 | git checkout master
5 |
6 | git submodule foreach git pull origin master
7 | git pull origin master
8 |
9 | git submodule foreach git merge develop
10 | git merge develop
11 |
12 | git submodule foreach git checkout develop
13 | git checkout develop
14 |
15 | git submodule foreach git pull origin develop
16 | git pull origin develop
17 |
--------------------------------------------------------------------------------
/probemanager/LICENSE:
--------------------------------------------------------------------------------
1 | ../LICENSE
--------------------------------------------------------------------------------
/probemanager/README.rst:
--------------------------------------------------------------------------------
1 | ../README.rst
--------------------------------------------------------------------------------
/probemanager/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/api/__init__.py
--------------------------------------------------------------------------------
/probemanager/api/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ApiConfig(AppConfig):
5 | name = 'api'
6 |
--------------------------------------------------------------------------------
/probemanager/api/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/api/migrations/__init__.py
--------------------------------------------------------------------------------
/probemanager/api/pagination.py:
--------------------------------------------------------------------------------
1 | from rest_framework.pagination import LimitOffsetPagination
2 |
3 |
4 | class StandardResultsSetPagination(LimitOffsetPagination):
5 | default_limit = 20
6 | max_limit = 100
7 |
--------------------------------------------------------------------------------
/probemanager/api/serializers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User, Group
2 | from django_celery_beat.models import PeriodicTask, CrontabSchedule
3 | from rest_framework import serializers
4 |
5 | from core.models import Server, SshKey, Configuration, Job
6 |
7 |
8 | class UserSerializer(serializers.ModelSerializer):
9 | class Meta:
10 | model = User
11 | fields = ('url', 'username', 'email', 'groups')
12 |
13 |
14 | class GroupSerializer(serializers.ModelSerializer):
15 | class Meta:
16 | model = Group
17 | fields = ('url', 'name')
18 |
19 |
20 | class PeriodicTaskSerializer(serializers.ModelSerializer):
21 | class Meta:
22 | model = PeriodicTask
23 | fields = "__all__"
24 |
25 |
26 | class CrontabScheduleSerializer(serializers.ModelSerializer):
27 | class Meta:
28 | model = CrontabSchedule
29 | fields = "__all__"
30 |
31 |
32 | class ServerSerializer(serializers.ModelSerializer):
33 | class Meta:
34 | model = Server
35 | fields = "__all__"
36 |
37 |
38 | class SshKeySerializer(serializers.ModelSerializer):
39 | class Meta:
40 | model = SshKey
41 | fields = "__all__"
42 |
43 |
44 | class ConfigurationSerializer(serializers.ModelSerializer):
45 | class Meta:
46 | model = Configuration
47 | fields = "__all__"
48 |
49 |
50 | class ConfigurationUpdateSerializer(serializers.ModelSerializer):
51 | class Meta:
52 | model = Configuration
53 | fields = ('value', )
54 |
55 |
56 | class JobSerializer(serializers.ModelSerializer):
57 | class Meta:
58 | model = Job
59 | fields = "__all__"
60 |
--------------------------------------------------------------------------------
/probemanager/api/urls.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | import os
3 |
4 | from django.apps.registry import apps
5 | from django.conf import settings
6 | from rest_framework import routers
7 |
8 | from . import views
9 |
10 | router = routers.DefaultRouter()
11 |
12 | router.register(r'^admin/users', views.UserViewSet)
13 | router.register(r'^admin/groups', views.GroupViewSet)
14 | router.register(r'^core/server', views.ServerViewSet, base_name="core")
15 | router.register(r'^core/sshkey', views.SshKeyViewSet, base_name="core")
16 | router.register(r'^core/configuration', views.ConfigurationViewSet, base_name="core")
17 | router.register(r'^core/job', views.JobViewSet, base_name="core")
18 | router.register(r'^celerybeat/crontabschedule', views.CrontabScheduleViewSet)
19 | router.register(r'^celerybeat/periodictask', views.PeriodicTaskViewSet)
20 |
21 |
22 | for app in apps.get_app_configs():
23 | path = settings.BASE_DIR + "/" + app.label + "/api/urls.py"
24 | if os.path.isfile(path):
25 | my_attr = getattr(importlib.import_module(app.label + ".api.urls"), 'urls_to_register')
26 | for url in my_attr:
27 | router.register(*url, base_name=app.label)
28 |
29 | urlpatterns = list()
30 | urlpatterns += router.urls
31 |
--------------------------------------------------------------------------------
/probemanager/api/views.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from django.conf import settings
4 | from django.contrib.auth.models import User, Group
5 | from django_celery_beat.models import PeriodicTask, CrontabSchedule
6 | from rest_framework import mixins
7 | from rest_framework import status
8 | from rest_framework import viewsets
9 | from rest_framework.response import Response
10 | from rest_framework.decorators import action
11 |
12 | from core.models import Server, SshKey, Configuration, Job
13 | from .serializers import UserSerializer, GroupSerializer, CrontabScheduleSerializer, \
14 | PeriodicTaskSerializer, ServerSerializer, SshKeySerializer, ConfigurationSerializer, \
15 | ConfigurationUpdateSerializer, JobSerializer
16 |
17 |
18 | class UserViewSet(viewsets.ModelViewSet):
19 | """
20 | API endpoint that allows users to be viewed or edited.
21 | """
22 | queryset = User.objects.all().order_by('-date_joined')
23 | serializer_class = UserSerializer
24 |
25 |
26 | class GroupViewSet(viewsets.ModelViewSet):
27 | """
28 | API endpoint that allows groups to be viewed or edited.
29 | """
30 | queryset = Group.objects.all()
31 | serializer_class = GroupSerializer
32 |
33 |
34 | class PeriodicTaskViewSet(viewsets.ModelViewSet):
35 | queryset = PeriodicTask.objects.all()
36 | serializer_class = PeriodicTaskSerializer
37 |
38 |
39 | class CrontabScheduleViewSet(viewsets.ModelViewSet):
40 | queryset = CrontabSchedule.objects.all()
41 | serializer_class = CrontabScheduleSerializer
42 |
43 |
44 | class ServerViewSet(viewsets.ModelViewSet):
45 | queryset = Server.objects.all()
46 | serializer_class = ServerSerializer
47 |
48 | @action(detail=True)
49 | def test_connection(self, request, pk=None):
50 | obj = self.get_object()
51 | if obj.become:
52 | response = obj.test_become()
53 | else:
54 | response = obj.test()
55 | return Response(response)
56 |
57 |
58 | class ConfigurationViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
59 | queryset = Configuration.objects.all()
60 | serializer_class = ConfigurationSerializer
61 |
62 | def update(self, request, pk=None):
63 | return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
64 |
65 | def partial_update(self, request, pk=None):
66 | conf = self.get_object()
67 | serializer = ConfigurationUpdateSerializer(conf, data=request.data)
68 | if serializer.is_valid():
69 | serializer.save()
70 | return Response(serializer.data)
71 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
72 |
73 |
74 | class SshKeyViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin,
75 | viewsets.GenericViewSet):
76 | queryset = SshKey.objects.all()
77 | serializer_class = SshKeySerializer
78 |
79 | def create(self, request):
80 | with open(settings.BASE_DIR + "/ssh_keys/" + request.data['name'], 'w', encoding="utf_8") as f:
81 | f.write(request.data['file'])
82 | os.chmod(settings.BASE_DIR + "/ssh_keys/" + request.data['name'], 0o640)
83 | sshkey = SshKey(name=request.data['name'], file="ssh_keys/" + request.data['name'])
84 | sshkey.save()
85 | return Response(status=204)
86 |
87 |
88 | class JobViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
89 | queryset = Job.objects.all()
90 | serializer_class = JobSerializer
91 |
--------------------------------------------------------------------------------
/probemanager/core/README.rst:
--------------------------------------------------------------------------------
1 | ****
2 | Core
3 | ****
4 |
5 | Presentation
6 | ============
7 |
8 | Application for general things about project and probe.
9 |
10 | Features
11 | --------
12 |
13 | * Jobs. To see the results of asynchronous tasks.
14 | * List of the operating systems supported by this application.
15 | * Server, remote server.
16 | * Ssh Key, to authenticate on the remote server.
17 | * General configuration of this application. (Pushbullet API KEY, MISP API KEY, SPLUNK HOST ...)
18 | * Generic Probe.
19 | * Generic Probe configuration.
20 |
21 | Usage
22 | =====
23 |
24 | Administration Page of the module :
25 | -----------------------------------
26 |
27 | .. image:: https://raw.githubusercontent.com/treussart/ProbeManager/master/probemanager/core/data/admin-index.png
28 | :align: center
29 | :width: 80%
30 |
31 | Page to add a remote server :
32 | -----------------------------
33 |
34 | .. image:: https://raw.githubusercontent.com/treussart/ProbeManager/master/probemanager/core/data/admin-server-add.png
35 | :align: center
36 | :width: 70%
37 |
38 | * Name: Give a unique name for this server, example: server-tap1.
39 | * Host: The IP address or Hostname.
40 | * Os: The Operating system (Debian or Ubuntu).
41 | * Remote User: The user used for the connection 'ssh user@hostname'
42 | * Remote port: The remote port 'ssh -p 22'.
43 | * Become: True or False, if the user needs to elevate his privileges to be root.
44 | * Become method:
45 | * Become user: Often root, but you can use another user with fewer privileges than root.
46 | * Become pass: The password for the user you will want to become, if necessary.
47 | * Ssh private key file: The private key file to authenticate (not encrypted, the tool will encrypt it).
48 |
49 |
50 | Page to add a SSH Key :
51 | -----------------------
52 |
53 | .. image:: https://raw.githubusercontent.com/treussart/ProbeManager/master/probemanager/core/data/admin-sshkey-add.png
54 | :align: center
55 | :width: 60%
56 |
57 | * Name: A name for this key. (In the API, the name is the name of the file)
58 | * File: The file to upload. (In the API, the file is the text file of the private key)
59 |
60 | The SSH key must not be encrypted, the tool will encrypt it with the secret key of Django.
61 |
62 | Page to see results of asynchronous jobs :
63 | ------------------------------------------
64 |
65 | .. image:: https://raw.githubusercontent.com/treussart/ProbeManager/master/probemanager/core/data/admin-jobs-full.png
66 | :align: center
67 | :width: 90%
68 |
--------------------------------------------------------------------------------
/probemanager/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/__init__.py
--------------------------------------------------------------------------------
/probemanager/core/admin.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from django.contrib import admin
4 | from django.contrib import messages
5 | from django_celery_beat.models import SolarSchedule, IntervalSchedule
6 |
7 | from .forms import ServerForm
8 | from .models import SshKey, Server, Job, Configuration
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 |
13 | class ServerAdmin(admin.ModelAdmin):
14 | form = ServerForm
15 |
16 | def save_model(self, request, obj, form, change):
17 | super().save_model(request, obj, form, change)
18 | if obj.become:
19 | response = obj.test_become()
20 | else:
21 | response = obj.test()
22 | if response['status']:
23 | messages.add_message(request, messages.SUCCESS, "Connection to the server OK")
24 | else:
25 | messages.add_message(request, messages.ERROR, "Connection to the server Failed")
26 |
27 |
28 | class JobAdmin(admin.ModelAdmin):
29 | list_filter = ('status', 'completed', 'probe')
30 | list_display = ('name', 'probe', 'status', 'created', 'completed', 'result')
31 | list_display_links = None
32 |
33 | class Media:
34 | js = (
35 | 'core/js/reload.js',
36 | )
37 |
38 | def has_add_permission(self, request):
39 | return False
40 |
41 |
42 | admin.site.register(SshKey)
43 | admin.site.register(Server, ServerAdmin)
44 | admin.site.register(Job, JobAdmin)
45 | admin.site.register(Configuration)
46 |
47 | admin.site.unregister(SolarSchedule)
48 | admin.site.unregister(IntervalSchedule)
49 |
--------------------------------------------------------------------------------
/probemanager/core/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class CoreConfig(AppConfig):
5 | name = 'core'
6 |
--------------------------------------------------------------------------------
/probemanager/core/data/admin-index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/data/admin-index.png
--------------------------------------------------------------------------------
/probemanager/core/data/admin-jobs-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/data/admin-jobs-full.png
--------------------------------------------------------------------------------
/probemanager/core/data/admin-jobs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/data/admin-jobs.png
--------------------------------------------------------------------------------
/probemanager/core/data/admin-server-add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/data/admin-server-add.png
--------------------------------------------------------------------------------
/probemanager/core/data/admin-sshkey-add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/data/admin-sshkey-add.png
--------------------------------------------------------------------------------
/probemanager/core/exceptions.py:
--------------------------------------------------------------------------------
1 | class ProbeManagerError(Exception):
2 | def __init__(self, message):
3 | super().__init__(message)
4 | self.message = message
5 |
--------------------------------------------------------------------------------
/probemanager/core/fixtures/test-core-probe.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "core.probe",
4 | "pk": 1,
5 | "fields": {
6 | "name": "probe1",
7 | "description": "test",
8 | "created_date": "2017-09-23T21:00:53.094Z",
9 | "rules_updated_date": null,
10 | "type": "",
11 | "secure_deployment": true,
12 | "scheduled_rules_deployment_enabled": true,
13 | "scheduled_rules_deployment_crontab": 1,
14 | "server": 1,
15 | "installed": true
16 | }
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/probemanager/core/fixtures/test-core-probeconfiguration.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "core.probeconfiguration",
4 | "pk": 1,
5 | "fields": {
6 | "name": "conf1"
7 | }
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/probemanager/core/fixtures/test-core-sshkey.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "core.sshkey",
4 | "pk": 1,
5 | "fields": {
6 | "name": "test",
7 | "file": "ssh_keys/test.com_rsa"
8 | }
9 | }
10 | ]
11 |
--------------------------------------------------------------------------------
/probemanager/core/forms.py:
--------------------------------------------------------------------------------
1 | from django.forms import ModelForm, PasswordInput
2 |
3 | from .models import Server
4 |
5 |
6 | class ServerForm(ModelForm):
7 | class Meta:
8 | model = Server
9 | fields = (
10 | 'name', 'host', 'os', 'remote_user', 'remote_port', 'become', 'become_method', 'become_user',
11 | 'become_pass', 'ssh_private_key_file')
12 | widgets = {
13 | 'become_pass': PasswordInput(),
14 | }
15 |
--------------------------------------------------------------------------------
/probemanager/core/git.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import subprocess
3 |
4 | from django.conf import settings
5 |
6 | logger = logging.getLogger(__name__)
7 |
8 |
9 | def git_tag(git_root):
10 | command = [settings.GIT_BINARY, 'describe', '--tags', '--always']
11 | command_date = [settings.GIT_BINARY, 'log', '-n 1', '--date=short', '--pretty=%ad']
12 | p = subprocess.Popen(command, cwd=git_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
13 | universal_newlines=True)
14 | out, err = p.communicate()
15 | if err: # pragma: no cover
16 | logger.exception(str(err))
17 | p_date = subprocess.Popen(command_date, cwd=git_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
18 | universal_newlines=True)
19 | out_date, err_date = p_date.communicate()
20 | out_date.replace('-', '.')
21 | if err_date: # pragma: no cover
22 | logger.exception(str(err_date))
23 | return out.strip() + "-" + out_date.strip()
24 |
--------------------------------------------------------------------------------
/probemanager/core/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/migrations/__init__.py
--------------------------------------------------------------------------------
/probemanager/core/models.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | from django.conf import settings
5 | from django.db import models
6 | from django.utils import timezone
7 | from django_celery_beat.models import CrontabSchedule
8 | from paramiko.rsakey import RSAKey
9 |
10 | from .modelsmixins import CommonMixin
11 | from .ssh import execute
12 | from .utils import encrypt
13 |
14 | logger = logging.getLogger(__name__)
15 |
16 |
17 | class Job(CommonMixin, models.Model):
18 | STATUS_CHOICES = (
19 | ('In progress', 'In progress'),
20 | ('Completed', 'Completed'),
21 | ('Cancelled', 'Cancelled'),
22 | ('Error', 'Error'),
23 | )
24 | name = models.CharField(max_length=255)
25 | probe = models.CharField(max_length=255, verbose_name="Probe / URL")
26 | status = models.CharField(max_length=255, choices=STATUS_CHOICES)
27 | result = models.TextField(null=True, default=None, editable=False)
28 | created = models.DateTimeField(default=timezone.now)
29 | completed = models.DateTimeField(null=True, blank=True)
30 |
31 | class Meta:
32 | ordering = ('-created',)
33 |
34 | def __str__(self):
35 | return str(self.name)
36 |
37 | @classmethod
38 | def create_job(cls, name, probe_name):
39 | job = Job(name=name, probe=probe_name, status='In progress', created=timezone.now())
40 | job.save()
41 | return job
42 |
43 | def update_job(self, result, status):
44 | self.result = result
45 | self.status = status
46 | self.completed = timezone.now()
47 | self.save()
48 |
49 |
50 | class OsSupported(CommonMixin, models.Model):
51 | """
52 | Set of operating system name. For now, just debian is available.
53 | """
54 | name = models.CharField(max_length=100, unique=True)
55 |
56 | def __str__(self):
57 | return str(self.name)
58 |
59 |
60 | class SshKey(models.Model):
61 | """
62 | Set of Ssh keys, To connect to the remote server.
63 | """
64 | name = models.CharField(max_length=400, unique=True, blank=False, null=False)
65 | file = models.FileField(upload_to='ssh_keys/')
66 |
67 | def __str__(self):
68 | return str(self.name)
69 |
70 | def save(self, **kwargs):
71 | super().save(**kwargs)
72 | rsakey = RSAKey.from_private_key_file(settings.MEDIA_ROOT + "/" + self.file.name)
73 | rsakey.write_private_key_file(settings.MEDIA_ROOT + "/" + self.file.name, settings.SECRET_KEY)
74 | os.chmod(settings.MEDIA_ROOT + "/" + self.file.name, 0o640)
75 |
76 |
77 | class Server(CommonMixin, models.Model):
78 | """
79 | Server on which is deployed the Probes.
80 | """
81 | name = models.CharField(max_length=400, unique=True, default="")
82 | host = models.CharField(max_length=400, unique=True, default="localhost")
83 | os = models.ForeignKey(OsSupported, default=0, on_delete=models.CASCADE)
84 | remote_user = models.CharField(max_length=400, blank=True, default='admin')
85 | remote_port = models.IntegerField(blank=True, default=22)
86 | ssh_private_key_file = models.ForeignKey(SshKey, on_delete=models.CASCADE)
87 | become = models.BooleanField(default=False, blank=True)
88 | become_method = models.CharField(max_length=400, blank=True, default='sudo')
89 | become_user = models.CharField(max_length=400, blank=True, default='root')
90 | become_pass = models.CharField(max_length=400, blank=True, null=True)
91 |
92 | def __str__(self):
93 | return str(self.name) + ' - ' + str(self.host) + ', Os : ' + str(self.os)
94 |
95 | def save(self, **kwargs):
96 | if self.become_pass:
97 | self.become_pass = encrypt(self.become_pass)
98 | super().save(**kwargs)
99 |
100 | def test(self):
101 | command = "cat /etc/hostname"
102 | tasks = {"test_connection": command}
103 | try:
104 | response = execute(self, tasks)
105 | except Exception as e:
106 | logger.exception("Error during the connection to the server.")
107 | return {'status': False, 'errors': str(e)}
108 | else:
109 | logger.debug("output : " + str(response))
110 | return {'status': True}
111 |
112 | def test_become(self):
113 | command = "service ssh status"
114 | tasks = {"test_connection_and_become": command}
115 | try:
116 | response = execute(self, tasks, become=True)
117 | except Exception as e:
118 | logger.exception("Error during the connection to the server.")
119 | return {'status': False, 'errors': str(e)}
120 | else:
121 | logger.debug("output : " + str(response))
122 | return {'status': True}
123 |
124 |
125 | class Probe(CommonMixin, models.Model):
126 | """
127 | A probe is an IDS.
128 | """
129 | # General
130 | name = models.CharField(max_length=400, unique=True, blank=False, null=False)
131 | description = models.CharField(max_length=400, blank=True, default="")
132 | created_date = models.DateTimeField(default=timezone.now, editable=False)
133 | rules_updated_date = models.DateTimeField(blank=True, null=True, editable=False)
134 | type = models.CharField(max_length=400, blank=True, default='', editable=False)
135 | subtype = models.CharField(max_length=400, blank=True, null=True, editable=False)
136 | secure_deployment = models.BooleanField(default=True)
137 | scheduled_rules_deployment_enabled = models.BooleanField(default=False)
138 | scheduled_rules_deployment_crontab = models.ForeignKey(CrontabSchedule, related_name='crontabschedule_rules',
139 | blank=True, null=True, on_delete=models.CASCADE)
140 | scheduled_check_enabled = models.BooleanField(default=True)
141 | scheduled_check_crontab = models.ForeignKey(CrontabSchedule, related_name='crontabschedule_check', blank=True,
142 | null=True, on_delete=models.CASCADE)
143 | server = models.ForeignKey(Server, on_delete=models.CASCADE)
144 | installed = models.BooleanField('Probe Already installed', default=False)
145 |
146 | def __str__(self):
147 | return str(self.name)
148 |
149 | def uptime(self):
150 | if self.installed:
151 | if self.server.os.name == 'debian' or self.server.os.name == 'ubuntu':
152 | command = "service " + self.type.lower() + " status | grep since"
153 | else: # pragma: no cover
154 | raise NotImplementedError
155 | tasks = {"uptime": command}
156 | try:
157 | response = execute(self.server, tasks, become=True)
158 | except Exception as e:
159 | logger.exception("Error during the uptime")
160 | return 'Failed to get the uptime on the host : ' + str(e)
161 | else:
162 | logger.debug("output : " + str(response))
163 | return response['uptime']
164 | else:
165 | return 'Not installed'
166 |
167 | def restart(self):
168 | if self.server.os.name == 'debian' or self.server.os.name == 'ubuntu':
169 | command = "service " + self.__class__.__name__.lower() + " restart"
170 | else: # pragma: no cover
171 | raise NotImplementedError
172 | tasks = {"restart": command}
173 | try:
174 | response = execute(self.server, tasks, become=True)
175 | except Exception:
176 | logger.exception("Error during restart")
177 | return {'status': False, 'errors': "Error during restart"}
178 | else:
179 | logger.debug("output : " + str(response))
180 | return {'status': True}
181 |
182 | def start(self):
183 | if self.server.os.name == 'debian' or self.server.os.name == 'ubuntu':
184 | command = "service " + self.__class__.__name__.lower() + " start"
185 | else: # pragma: no cover
186 | raise NotImplementedError
187 | tasks = {"start": command}
188 | try:
189 | response = execute(self.server, tasks, become=True)
190 | except Exception:
191 | logger.exception("Error during start")
192 | return {'status': False, 'errors': "Error during start"}
193 | else:
194 | logger.debug("output : " + str(response))
195 | return {'status': True}
196 |
197 | def stop(self):
198 | if self.server.os.name == 'debian' or self.server.os.name == 'ubuntu':
199 | command = "service " + self.__class__.__name__.lower() + " stop"
200 | else: # pragma: no cover
201 | raise NotImplementedError
202 | tasks = {"stop": command}
203 | try:
204 | response = execute(self.server, tasks, become=True)
205 | except Exception:
206 | logger.exception("Error during stop")
207 | return {'status': False, 'errors': "Error during stop"}
208 | else:
209 | logger.debug("output : " + str(response))
210 | return {'status': True}
211 |
212 | def status(self):
213 | if self.installed:
214 | if self.server.os.name == 'debian' or self.server.os.name == 'ubuntu':
215 | command = "service " + self.__class__.__name__.lower() + " status"
216 | else: # pragma: no cover
217 | raise NotImplementedError
218 | tasks = {"status": command}
219 | try:
220 | response = execute(self.server, tasks, become=True)
221 | except Exception:
222 | logger.exception('Failed to get status')
223 | return 'Failed to get status'
224 | else:
225 | logger.debug("output : " + str(response))
226 | return response['status']
227 | else:
228 | return 'Not installed'
229 |
230 | def reload(self):
231 | if self.server.os.name == 'debian' or self.server.os.name == 'ubuntu':
232 | command = "service " + self.__class__.__name__.lower() + " reload"
233 | else: # pragma: no cover
234 | raise NotImplementedError
235 | tasks = {"reload": command}
236 | try:
237 | response = execute(self.server, tasks, become=True)
238 | except Exception:
239 | logger.exception("Error during reload")
240 | return {'status': False, 'errors': "Error during reload"}
241 | else:
242 | logger.debug("output : " + str(response))
243 | return {'status': True}
244 |
245 | @classmethod
246 | def get_by_name(cls, name):
247 | try:
248 | probe = cls.objects.get(name=name)
249 | except cls.DoesNotExist as e:
250 | logger.debug('Tries to access an object that does not exist : ' + str(e))
251 | return None
252 | return probe
253 |
254 |
255 | class ProbeConfiguration(CommonMixin, models.Model):
256 | """
257 | Configuration for a probe, Allows you to reuse the configuration.
258 | """
259 | # General
260 | name = models.CharField(max_length=100, unique=True, blank=False, null=False)
261 |
262 | def __str__(self):
263 | return str(self.name)
264 |
265 |
266 | class Configuration(models.Model):
267 | """
268 | Configuration for the application.
269 | """
270 | key = models.CharField(max_length=100, unique=True, blank=False, null=False)
271 | value = models.CharField(max_length=300, blank=True, null=False)
272 |
273 | def __str__(self):
274 | return str(self.key)
275 |
276 | @classmethod
277 | def get_value(cls, key):
278 | try:
279 | if cls.objects.get(key=key).value != "":
280 | return cls.objects.get(key=key).value
281 | else:
282 | return None
283 | except cls.DoesNotExist:
284 | return None
285 |
--------------------------------------------------------------------------------
/probemanager/core/modelsmixins.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | import logging
3 | import os
4 | import shutil
5 | import time
6 | from contextlib import contextmanager
7 |
8 | from django.conf import settings
9 |
10 |
11 | class CommonMixin:
12 |
13 | @classmethod
14 | def get_logger(cls):
15 | return logging.getLogger(__name__.split('.')[0] + '.' +
16 | os.path.basename(inspect.getsourcefile(cls)) + ':' + cls.__name__)
17 |
18 | @classmethod
19 | def get_all(cls):
20 | return cls.objects.all()
21 |
22 | @classmethod
23 | def get_by_id(cls, key):
24 | try:
25 | probe = cls.objects.get(id=key)
26 | except cls.DoesNotExist:
27 | cls.get_logger().warning('Tries to access an object that does not exist', exc_info=True)
28 | return None
29 | return probe
30 |
31 | @classmethod
32 | def get_last(cls):
33 | try:
34 | obj = cls.objects.last()
35 | except cls.DoesNotExist:
36 | cls.get_logger().warning('Tries to access an object that does not exist', exc_info=True)
37 | return None
38 | return obj
39 |
40 | @classmethod
41 | def get_nbr(cls, nbr):
42 | return cls.objects.all()[:nbr]
43 |
44 | @classmethod
45 | @contextmanager
46 | def get_tmp_dir(cls, folder_name=None):
47 | if folder_name:
48 | tmp_dir = settings.BASE_DIR + '/tmp/' + os.path.basename(os.path.dirname(inspect.getsourcefile(cls))) + \
49 | '/' + cls.__name__ + '/' + str(folder_name) + '/' + str(time.time()) + '/'
50 | else:
51 | tmp_dir = settings.BASE_DIR + '/tmp/' + os.path.basename(os.path.dirname(inspect.getsourcefile(cls))) + \
52 | '/' + cls.__name__ + '/' + str(time.time()) + '/'
53 | try:
54 | if not os.path.exists(tmp_dir):
55 | os.makedirs(tmp_dir)
56 | yield tmp_dir
57 | finally:
58 | shutil.rmtree(tmp_dir, ignore_errors=True)
59 |
--------------------------------------------------------------------------------
/probemanager/core/notifications.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from smtplib import SMTPException
3 |
4 | import requests
5 | from django.conf import settings
6 | from django.contrib.auth.models import User
7 | from django.db.models.signals import post_save
8 | from django.dispatch import receiver
9 | from lxml import html as html_lxml
10 | from pushbullet import Pushbullet
11 | from pushbullet.errors import InvalidKeyError, PushError
12 |
13 | from .models import Configuration
14 |
15 | logger = logging.getLogger('core.notifications')
16 |
17 |
18 | def pushbullet(title, plain_body): # pragma: no cover
19 | if Configuration.get_value("PUSHBULLET_API_KEY"):
20 | try:
21 | pb = Pushbullet(Configuration.get_value("PUSHBULLET_API_KEY"))
22 | push = pb.push_note(title, plain_body)
23 | logger.debug(push)
24 | except InvalidKeyError:
25 | logger.exception('Wrong PUSHBULLET_API_KEY')
26 | except PushError:
27 | logger.exception('Pushbullet pro required - too many notifications generated')
28 |
29 |
30 | def splunk(html_body): # pragma: no cover
31 | if Configuration.get_value("SPLUNK_HOST"):
32 | if Configuration.get_value("SPLUNK_USER") and Configuration.get_value("SPLUNK_PASSWORD"):
33 | url = "https://" + Configuration.get_value(
34 | "SPLUNK_HOST") + ":8089/services/receivers/simple?source=ProbeManager&sourcetype=notification"
35 | r = requests.post(url, verify=False, data=html_body,
36 | auth=(Configuration.get_value("SPLUNK_USER"), Configuration.get_value("SPLUNK_PASSWORD")))
37 | else:
38 | url = "https://" + Configuration.get_value(
39 | "SPLUNK_HOST") + ":8089/services/receivers/simple?source=ProbeManager&sourcetype=notification"
40 | r = requests.post(url, verify=False, data=html_body)
41 | logger.debug("Splunk " + str(r.text))
42 |
43 |
44 | def email(title, plain_body, html_body): # pragma: no cover
45 | users = User.objects.all()
46 | if settings.DEFAULT_FROM_EMAIL:
47 | try:
48 | for user in users:
49 | if user.is_superuser:
50 | try:
51 | user.email_user(title, plain_body, html_message=html_body)
52 | except AttributeError:
53 | logger.exception("Error in sending email")
54 | except SMTPException:
55 | logger.exception("Error in sending email")
56 | except ConnectionRefusedError:
57 | logger.exception("Error in sending email")
58 |
59 |
60 | def send_notification(title, body, html=False): # pragma: no cover
61 | if html:
62 | plain_body = html_lxml.fromstring(body).text_content()
63 | html_body = body
64 | else:
65 | plain_body = body
66 | html_body = '
' + body + '
'
67 | # Pushbullet
68 | pushbullet(title, plain_body)
69 | # Splunk
70 | splunk(html_body)
71 | # Email
72 | email(title, plain_body, html_body)
73 |
74 |
75 | @receiver(post_save, sender=User)
76 | def my_handler(sender, instance, **kwargs): # pragma: no cover
77 | send_notification(sender.__name__ + " created", str(instance.username) + " - " + str(instance.email))
78 |
--------------------------------------------------------------------------------
/probemanager/core/ssh.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | import paramiko
5 | from django.conf import settings
6 |
7 | from .utils import decrypt
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 |
12 | def connection(server):
13 | client = paramiko.SSHClient()
14 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
15 | client.connect(hostname=server.host,
16 | username=server.remote_user,
17 | port=server.remote_port,
18 | key_filename=settings.MEDIA_ROOT + "/" + server.ssh_private_key_file.file.name,
19 | passphrase=settings.SECRET_KEY,
20 | )
21 | return client
22 |
23 |
24 | def execute(server, commands, become=False):
25 | result = dict()
26 | for command_name, command in commands.items():
27 | if become:
28 | if server.become:
29 | if server.become_pass is not None:
30 | password = decrypt(server.become_pass)
31 | command = " echo '" + password + \
32 | "' | " + server.become_method + " -S " + command
33 | else:
34 | command = server.become_method + " " + command
35 | else:
36 | raise Exception("Server cannot become", server.name)
37 | client = connection(server)
38 | stdin, stdout, stderr = client.exec_command(command)
39 | if stdout.channel.recv_exit_status() != 0:
40 | raise Exception("Command Failed",
41 | "Command: " + command_name +
42 | " Exitcode: " + str(stdout.channel.recv_exit_status()) +
43 | " Message: " + stdout.read().decode('utf-8') +
44 | " Error: " + str(stderr.readlines())
45 | )
46 | else:
47 | result[command_name] = stdout.read().decode('utf-8').replace('\n', '')
48 | if result[command_name] == '':
49 | result[command_name] = 'OK'
50 |
51 | client.close()
52 | return result
53 |
54 |
55 | def execute_copy(server, src, dest, put=True, become=False):
56 | result = dict()
57 | client = connection(server)
58 | ftp_client = client.open_sftp()
59 | try:
60 | if put:
61 | if become:
62 | if server.become:
63 | ftp_client.put(src, os.path.basename(dest))
64 | else:
65 | raise Exception("Server cannot become", server.name)
66 | else:
67 | ftp_client.put(src, dest)
68 | else:
69 | ftp_client.get(dest, src)
70 | except Exception as e:
71 | raise Exception("Command scp Failed",
72 | " Message: " + str(e)
73 | )
74 | result['copy'] = "OK"
75 | if become:
76 | if server.become:
77 | commands = {"mv": "mv " + os.path.basename(dest) + " " + dest}
78 | result['mv'] = execute(server, commands, become=True)
79 | ftp_client.close()
80 | client.close()
81 | return result
82 |
--------------------------------------------------------------------------------
/probemanager/core/static/core/bootstrap/css/bootstrap-reboot.min.css:
--------------------------------------------------------------------------------
1 | html{box-sizing:border-box;font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}*,::after,::before{box-sizing:inherit}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#868e96;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}
2 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */
--------------------------------------------------------------------------------
/probemanager/core/static/core/css/style.css:
--------------------------------------------------------------------------------
1 | /* The switch - the box around the slider */
2 | .switch {
3 | position: relative;
4 | display: inline-block;
5 | width: 60px;
6 | height: 34px;
7 | }
8 |
9 | /* Hide default HTML checkbox */
10 | .switch input {display:none;}
11 |
12 | /* The slider */
13 | .slider {
14 | position: absolute;
15 | cursor: pointer;
16 | top: 0;
17 | left: 0;
18 | right: 0;
19 | bottom: 0;
20 | background-color: #ccc;
21 | -webkit-transition: .4s;
22 | transition: .4s;
23 | }
24 |
25 | .slider:before {
26 | position: absolute;
27 | content: "";
28 | height: 26px;
29 | width: 26px;
30 | left: 4px;
31 | bottom: 4px;
32 | background-color: white;
33 | -webkit-transition: .4s;
34 | transition: .4s;
35 | }
36 |
37 | input:checked + .slider {
38 | background-color: #2196F3;
39 | }
40 |
41 | input:focus + .slider {
42 | box-shadow: 0 0 1px #2196F3;
43 | }
44 |
45 | input:checked + .slider:before {
46 | -webkit-transform: translateX(26px);
47 | -ms-transform: translateX(26px);
48 | transform: translateX(26px);
49 | }
50 |
51 | /* Rounded sliders */
52 | .slider.round {
53 | border-radius: 34px;
54 | }
55 |
56 | .slider.round:before {
57 | border-radius: 50%;
58 | }
--------------------------------------------------------------------------------
/probemanager/core/static/core/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/static/core/images/favicon.ico
--------------------------------------------------------------------------------
/probemanager/core/static/core/js/reload.js:
--------------------------------------------------------------------------------
1 |
2 | django.jQuery(document).ready(function(){
3 | django.jQuery(".object-tools").after("
");
4 | });
5 |
--------------------------------------------------------------------------------
/probemanager/core/tasks.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | import reprlib
3 |
4 | from celery import task
5 | from celery.utils.log import get_task_logger
6 |
7 | from .models import Probe, Job
8 | from .notifications import send_notification
9 |
10 | logger = get_task_logger(__name__)
11 |
12 | repr_instance = reprlib.Repr()
13 | repr_instance.maxstring = 200
14 |
15 |
16 | @task
17 | def deploy_rules(probe_name):
18 | job = Job.create_job('deploy_rules', probe_name)
19 | probe = Probe.get_by_name(probe_name)
20 | if probe is None:
21 | job.update_job("Error - probe is None - param id not set : " + str(probe_name), 'Error')
22 | return {"message": "Error - probe is None - param id not set : " + str(probe_name)}
23 | if probe.subtype:
24 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
25 | else:
26 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
27 | probe = my_class.get_by_name(probe_name)
28 | try:
29 | response_deploy_rules = probe.deploy_rules()
30 | response_reload = probe.reload()
31 | if response_deploy_rules['status'] and response_reload['status']:
32 | job.update_job('Deployed rules successfully', 'Completed')
33 | elif not response_deploy_rules['status']:
34 | if 'errors' in response_deploy_rules:
35 | job.update_job('Error during the rules deployed',
36 | 'Error: ' + str(probe_name) + " - " +
37 | repr_instance.repr(response_deploy_rules['errors']))
38 | logger.error("task - deploy_rules : " + str(probe_name) + " - " + str(response_deploy_rules['errors']))
39 | return {"message": "Error for probe " + str(probe.name) + " to deploy rules",
40 | "exception": str(response_deploy_rules['errors'])}
41 | else:
42 | job.update_job('Error during the rules deployed', 'Error: ' + str(probe_name))
43 | logger.error("task - deploy_rules : " + str(probe_name))
44 | return {"message": "Error for probe " + str(probe.name) + " to deploy rules", "exception": " "}
45 | elif not response_reload['status']:
46 | job.update_job('Error during the rules deployed',
47 | 'Error: ' + str(probe_name) + repr_instance.repr(response_reload['errors']))
48 | logger.error("task - deploy_rules : " + str(probe_name) + " - " + str(response_reload['errors']))
49 | return {"message": "Error for probe " + str(probe.name) + " to deploy rules",
50 | "exception": str(response_reload['errors'])}
51 | except Exception as e:
52 | logger.exception('Error during the rules deployed')
53 | job.update_job(repr_instance.repr(e), 'Error')
54 | send_notification("Probe " + str(probe.name), str(e))
55 | return {"message": "Error for probe " + str(probe.name) + " to deploy rules", "exception": str(e)}
56 | return {"message": "Probe " + probe.name + ' deployed rules successfully'}
57 |
58 |
59 | @task
60 | def reload_probe(probe_name):
61 | job = Job.create_job('reload_probe', probe_name)
62 | probe = Probe.get_by_name(probe_name)
63 | if probe is None:
64 | job.update_job("Error - probe is None - param id not set : " + str(probe_name), 'Error')
65 | return {"message": "Error - probe is None - param id not set : " + str(probe_name)}
66 | if probe.subtype:
67 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
68 | else:
69 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
70 | probe = my_class.get_by_name(probe_name)
71 | if probe.scheduled_rules_deployment_enabled:
72 | try:
73 | response = probe.reload()
74 | if response['status']:
75 | job.update_job("task - reload_probe : " + str(probe_name), 'Completed')
76 | logger.info("task - reload_probe : " + str(probe_name))
77 | return {"message": "Probe " + str(probe.name) + " reloaded successfully"}
78 | else:
79 | job.update_job(repr_instance.repr(response['errors']), 'Error')
80 | return {"message": "Error for probe " + str(probe.name) + " to reload",
81 | "exception": str(response['errors'])}
82 | except Exception as e:
83 | logger.exception("Error for probe to reload")
84 | job.update_job(repr_instance.repr(e), 'Error')
85 | send_notification("Probe " + str(probe.name), str(e))
86 | return {"message": "Error for probe " + str(probe.name) + " to reload", "exception": str(e)}
87 | else:
88 | job.update_job("Not enabled to reload", 'Error')
89 | send_notification("Probe " + str(probe.name), "Not enabled to reload")
90 | return {"message": probe.name + " not enabled to reload"}
91 |
92 |
93 | @task
94 | def install_probe(probe_name):
95 | job = Job.create_job('install_probe', probe_name)
96 | probe = Probe.get_by_name(probe_name)
97 | if probe is None:
98 | job.update_job("Error - probe is None - param id not set : " + str(probe_name), 'Error')
99 | return {"message": "Error - probe is None - param id not set : " + str(probe_name)}
100 | if probe.subtype:
101 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
102 | else:
103 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
104 | probe = my_class.get_by_name(probe_name)
105 | try:
106 | response_install = probe.install()
107 | response_deploy_conf = probe.deploy_conf()
108 | response_deploy_rules = probe.deploy_rules()
109 | response_start = probe.start()
110 | except Exception as e:
111 | logger.exception("Error for probe to install")
112 | job.update_job(repr_instance.repr(e), 'Error')
113 | send_notification("Error for probe " + str(probe.name), str(e))
114 | return {"message": "Error for probe " + str(probe.name) + " to install", "exception": str(e)}
115 | if response_install['status'] and response_start['status'] and response_deploy_conf['status'] \
116 | and response_deploy_rules['status']:
117 | job.update_job('Probe ' + str(probe.name) + ' installed successfully', 'Completed')
118 | return {"message": "Probe " + str(probe.name) + " installed successfully"}
119 | else:
120 | job.update_job("Error for probe " + str(probe.name) + " to install", 'Error')
121 | return {"message": "Error for probe " + str(probe.name) + " to install"}
122 |
123 |
124 | @task
125 | def update_probe(probe_name):
126 | job = Job.create_job('update_probe', probe_name)
127 | probe = Probe.get_by_name(probe_name)
128 | if probe is None:
129 | job.update_job("Error - probe is None - param id not set : " + str(probe_name), 'Error')
130 | return {"message": "Error - probe is None - param id not set : " + str(probe_name)}
131 | if probe.subtype:
132 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
133 | else:
134 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
135 | probe = my_class.get_by_name(probe_name)
136 | try:
137 | response_update = probe.update()
138 | response_restart = probe.restart()
139 | except Exception as e:
140 | logger.exception("Error for probe to install")
141 | job.update_job(repr_instance.repr(e), 'Error')
142 | send_notification("Error for probe " + str(probe.name), str(e))
143 | return {"message": "Error for probe " + str(probe.name) + " to install", "exception": str(e)}
144 | if response_update['status'] and response_restart['status']:
145 | job.update_job(response_update + response_restart, 'Completed')
146 | return {"message": "Probe " + str(probe.name) + " updated successfully"}
147 | else:
148 | job.update_job("Error for probe " + str(probe.name) + " to update", 'Error')
149 | return {"message": "Error for probe " + str(probe.name) + " to update"}
150 |
151 |
152 | @task
153 | def check_probe(probe_name):
154 | job = Job.create_job('check_probe', probe_name)
155 | probe = Probe.get_by_name(probe_name)
156 | if probe is None:
157 | job.update_job("Error - probe is None - param id not set : " + str(probe_name), 'Error')
158 | return {"message": "Error - probe is None - param id not set : " + str(probe_name)}
159 | if probe.subtype:
160 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
161 | else:
162 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
163 | probe = my_class.get_by_name(probe_name)
164 | if probe.installed:
165 | try:
166 | response_status = probe.status()
167 | except Exception as e:
168 | logger.exception("Error for probe to check status")
169 | job.update_job(repr_instance.repr(e), 'Error')
170 | send_notification("Error for probe " + str(probe.name), str(e))
171 | return {"message": "Error for probe " + str(probe.name) + " to check status", "exception": str(e)}
172 | if response_status:
173 | if 'active (running)' in response_status:
174 | job.update_job("OK probe " + str(probe.name) + " is running", 'Completed')
175 | return {"message": "OK probe " + str(probe.name) + " is running"}
176 | else:
177 | job.update_job("KO probe " + str(probe.name) + " is not running", 'Completed')
178 | send_notification("probe KO", "Probe " + str(probe.name) + " is not running")
179 | return {"message": "KO probe " + str(probe.name) + " is not running"}
180 | else:
181 | job.update_job("Error for probe " + str(probe.name) + " to check status", 'Error')
182 | return {"message": "Error for probe " + str(probe.name) + " to check status"}
183 | else:
184 | job.update_job("Probe " + str(probe.name) + " not installed", 'Completed')
185 | return {"message": "Probe " + str(probe.name) + " not installed"}
186 |
--------------------------------------------------------------------------------
/probemanager/core/templates/core/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% block header %}
5 | {% load static %}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {% endblock %}
17 |
18 | {% block title %}Probe Manager{% endblock %}
19 |
20 |
21 |
22 |
23 |
24 |
55 |
56 |
57 | {% block content %}{% endblock %}
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/probemanager/core/templates/core/index.html:
--------------------------------------------------------------------------------
1 | {% extends "core/base.html" %}
2 |
3 | {% block title %}Home{% endblock %}
4 |
5 | {% block content %}
6 |
7 | {% if messages %}
8 |
9 | {% for message in messages %}
10 | - {{ message }}
11 | {% endfor %}
12 |
13 | {% endif %}
14 |
15 | {% for instance in instances %}
16 |
17 | {% with instance|add:"/home.html" as template %}
18 | {% include template %}
19 | {% endwith %}
20 |
21 | {% endfor %}
22 |
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/probemanager/core/templates/core/index_probe.html:
--------------------------------------------------------------------------------
1 | {% extends "core/base.html" %}
2 |
3 | {% block title %}{{ probe.type }}{% endblock %}
4 |
5 | {% block content %}
6 |
7 |
8 | Instance {% if probe.subtype %} {{ probe.subtype }} {% else %} {{ probe.type }} {% endif %} : {{ probe.name }}
9 |
10 |
11 | {% if probe.description != " " %}
12 | Description:
13 |
{{ probe.description }}
14 | {% endif %}
15 | Os : {{ probe.server.os }}
16 | Host : {{ probe.server.host }}
17 | Created date : {{ probe.created_date }}
18 | {% if probe.installed %}
19 | {% if probe.rules_updated_date %}
20 | Rules updated date : {{ probe.rules_updated_date }}
21 | {% else %}
22 | Rules updated date : Never
23 | {% endif %}
24 |
25 | Uptime {{ probe.type }} : {{ probe.uptime }}
26 |
{% load status %}
27 |
Refresh Instance Status
29 |
30 |
31 |
34 |
37 |
40 |
43 |
44 |
45 | {% endif %}
46 | {% if messages %}
47 |
48 |
49 | {% for message in messages %}
50 |
54 | {% endfor %}
55 |
56 | {% endif %}
57 |
58 |
71 |
72 |
73 | {% endblock %}
74 |
--------------------------------------------------------------------------------
/probemanager/core/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/templatetags/__init__.py
--------------------------------------------------------------------------------
/probemanager/core/templatetags/status.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | import logging
3 |
4 | from django import template
5 |
6 | from core.models import Probe
7 |
8 | logger = logging.getLogger(__name__)
9 | register = template.Library()
10 |
11 |
12 | @register.filter
13 | def status(probe_id):
14 | probe = Probe.get_by_id(probe_id)
15 | if probe is None: # pragma: no cover
16 | return {"message": "Error - probe is None - param id not set : " + str(probe_id)}
17 | if probe.subtype:
18 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
19 | else:
20 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
21 | probe = my_class.get_by_id(probe_id)
22 | response = probe.status()
23 | if 'running' in response:
24 | return 'success'
25 | else:
26 | return 'danger'
27 |
--------------------------------------------------------------------------------
/probemanager/core/templatetags/version.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from django import template
4 | from django.conf import settings
5 |
6 | register = template.Library()
7 |
8 |
9 | @register.simple_tag
10 | def version(app):
11 | if app == 'ProbeManager':
12 | with open(os.path.join(settings.BASE_DIR + '/version.txt')) as f:
13 | return f.read().strip()
14 | else:
15 | with open(os.path.join(settings.BASE_DIR + "/" + app['app_label'] + '/version.txt')) as f:
16 | return f.read().strip()
17 |
18 |
19 | @register.filter
20 | def test_version(app):
21 | if os.path.isfile(os.path.join(settings.BASE_DIR + "/" + app['app_label'] + '/version.txt')):
22 | return True
23 | else:
24 | return False
25 |
--------------------------------------------------------------------------------
/probemanager/core/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/core/tests/__init__.py
--------------------------------------------------------------------------------
/probemanager/core/tests/test_api.py:
--------------------------------------------------------------------------------
1 | """ venv/bin/python probemanager/manage.py test core.tests.test_api --settings=probemanager.settings.dev """
2 | from django.contrib.auth.models import User
3 | from rest_framework import status
4 | from rest_framework.test import APIClient
5 | from rest_framework.test import APITestCase
6 |
7 | from core.models import Configuration, Server
8 |
9 |
10 | class APITest(APITestCase):
11 | fixtures = ['init', 'crontab', 'test-core-secrets']
12 |
13 | def setUp(self):
14 | self.client = APIClient()
15 | User.objects.create_superuser(username='testuser', password='12345', email='testuser@test.com')
16 | if not self.client.login(username='testuser', password='12345'):
17 | self.assertRaises(Exception("Not logged"))
18 |
19 | def tearDown(self):
20 | self.client.logout()
21 |
22 | def test_server(self):
23 | response = self.client.get('/api/v1/core/server/1/test_connection/')
24 | self.assertEqual(response.status_code, status.HTTP_200_OK)
25 | self.assertEqual(response.data, {'status': True})
26 | server = Server.get_by_id(1)
27 | server.become = False
28 | server.save()
29 | response = self.client.get('/api/v1/core/server/1/test_connection/')
30 | self.assertEqual(response.status_code, status.HTTP_200_OK)
31 | self.assertEqual(response.data, {'status': True})
32 |
33 | def test_configuration(self):
34 | response = self.client.get('/api/v1/core/configuration/')
35 | self.assertEqual(response.status_code, status.HTTP_200_OK)
36 | self.assertEqual(response.data['count'], 6)
37 |
38 | data_put = {'value': 'test'}
39 | data_patch = {'value': 'test'}
40 | data_patch_2 = {'key': 'test'}
41 |
42 | self.assertEqual(Configuration.objects.get(key="SPLUNK_USER").value, "")
43 |
44 | response = self.client.put('/api/v1/core/configuration/' + str(Configuration.objects.get(key="SPLUNK_USER").id)
45 | + '/', data_put)
46 | self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
47 | self.assertEqual(Configuration.objects.get(key="SPLUNK_USER").value, "")
48 |
49 | response = self.client.patch('/api/v1/core/configuration/' +
50 | str(Configuration.objects.get(key="SPLUNK_USER").id) + '/', data_patch)
51 | self.assertEqual(response.status_code, status.HTTP_200_OK)
52 | self.assertEqual(Configuration.objects.get(key="SPLUNK_USER").value, "test")
53 |
54 | response = self.client.patch('/api/v1/core/configuration/' +
55 | str(Configuration.objects.get(key="SPLUNK_USER").id) + '/', data_patch_2)
56 | self.assertEqual(response.status_code, status.HTTP_200_OK)
57 | self.assertEqual(Configuration.objects.get(key="SPLUNK_USER").key, "SPLUNK_USER")
58 |
59 | response = self.client.get('/api/v1/core/configuration/')
60 | self.assertEqual(response.status_code, status.HTTP_200_OK)
61 | self.assertEqual(response.data['count'], 6)
62 |
63 | def test_sshkey(self):
64 | response = self.client.get('/api/v1/core/sshkey/')
65 | self.assertEqual(response.status_code, status.HTTP_200_OK)
66 | self.assertEqual(response.data['count'], 1)
67 |
68 | data = {"name": "test.com_rsa", "file": """-----BEGIN RSA PRIVATE KEY-----
69 | MIIJKAIBAAKCAgEA2GXuC9yTY9y7fNSolEYvdFjyjw7dBcU4TPgTU6ppxddNYH5Z
70 | SQ5qZOosKsGCjKeSP1gVaMYIULebPPB/Hs1t257nC//Gx46s2MxqWK9l4j6gLFzl
71 | eQETlUX8Yq7Ig95MfMUZXOf2lADtmPoxwW9hSalitPM1ilmXyZ8Br+6tfuKwfORJ
72 | Egnc7AiZGIsMPyatfIVHDQ1jT29w0CicbTZLkznIM9Ujk0Dy/LPs83a/w5OheVX2
73 | I7tqi3w+fhG/hd8GsntIr87L/R0MuegGnpwECtqkOvk6v6G3UiwScA1X73inXf56
74 | 3+KRaNjqH4SSX1F8KN9vsyt0Ua7ICfE+LJ2DwC8OUDORiVEVItX4AfTt7t7N/gTW
75 | L9TwImqybqflR4+q7emJUzYn7Vv9JxtHnhoym8Uuj8jU3X0MCHOrvcXhHZ5sHDEr
76 | sRdeSNpDam69vD7w4YYV/khXpUQGfCQw4l+htpAqjBSCNrMpIAv87yAUQUxCl3Wv
77 | Y9MUMNCx466nqxmG8cnZ8pTBmc8bUWj0WVstyuIjxS4yxbNWxkCu5GbfCPBUaMGP
78 | WIl/wNodbtjDxW2N1OD3loHdlq9A79fy6mhQj4dLt8PvIDYt7Of/tBqSn8ZEFsLS
79 | 2iwgPLFuww46bjw22euqY4RGfGspHUzGEfA5NfPZYz5hH6/sSV/7D52mbN0CAwEA
80 | AQKCAgEAsz6zEDY0C/rRfhP0U2VTd28Z86+fGmGDQhYWhC3bEVpGqI/fyyjarh5e
81 | WUgSqAlBlaCTk0a9qoZ7Wt3mnhARWGJmBUVnVPL0b1vbFvyqSt4O9NA576IZo4Lm
82 | DKO0Sa5/8rWcTZ2CXJPsOtO7FPv6PPbGYRY3mhKeLQ69agoswbZp8/lwITX0Pbrd
83 | fTvn+ANEnqkS8lfNlAW+D14kPD5GGXw6Pdzla9rXqsQqmHwbWZfWn9e5W12pYkKW
84 | zPxMhUn4lSyTR7TmuREv8mmj2gtnOcpjUMoShJsiazlASHp1BVIOaEgbZYmZYpyS
85 | SWsZh0TKsFxrfKCY5/P7lGi0VGZgsu/y67Q00Z72/rzQ2ROls/ysrZladA16ip0H
86 | PyCLglYlw12AZQjje1UJXVljdMfh8vbZS2p6V6z2hHVS9v2jHnRrwnu3hbc8kNcR
87 | dxfggUs4XZKKwaRb9z4mVw6qFL4c2ajpM8wlsplrWuIyEgyHGeAwewkG+adowUz/
88 | xQE709saNngbjuddfF8UNfCNA4nQv62f/rV04kUvjECthPIclhqmvbE09HYmdcrP
89 | qePvmniN1wOIqmfVS1oHMRCKw9UxZ+cosaulhd650c0/QjCO57A2c0gjl2cl5+Qp
90 | gESMgAmN3df7WeoW2cuK1oyOSOMk9ozfxP7w2wu5o5MVZBbyTyECggEBAP45gkH9
91 | YsAYE/l7dSwn67NnXJWpWpS2P/1EF/ExwKPzYVfivjTivVLlk8P8JQi34WvE/bRZ
92 | uWzlzrOTTJ++3R4u0G+QWUjDk0hntTd8Qy+YIVrzFE4VBlHfiZfcw6Yclnj6k+B5
93 | sC8ytuE73gpnbLoArJc5yi+qPk/jWsA+pRzPnUsYOLqSCuuFYpcwYQ3w/Yr7PW8b
94 | 6sCDUPHVSLhroxnCYsV9gJaAC7KExal0mYpxndcBkTPR1ujwcKJZWV4jz0IhF++8
95 | rgsNLUoB5sHRgDX/i6qTkNbnYI/z05IV/DHOZLXXpD0N49MJTav2L4JvBzaMxjZB
96 | N4h3X4syoZ+oBbUCggEBANnoy+3tGhM5KIQyxCfMhtWWj6yagyrGjtJ5N6uS1ln6
97 | U+4ppuLZwAR3nxlJePyMCSvENUJL8aPHNDNYG/V/qvak3uEQ9EyLZre9PiJfAx4n
98 | 87aT7bFwndBFTWr6OXtEmqlRcHcawhSya8NJ4cIec8IbiLmZqLfCxY66RovV6XdN
99 | 6yytXhsS9S/zkOeCsVQ1/XbGYePiX7RC/G4kCkns2Be65N/5bSrt4cn3NdxMa5CG
100 | Vny2nctWiqnJIyvd/eaRqkSgRrUGPLVZ2t/ctX11GQ9RHQwrpt0VlwA0DKvNRmBX
101 | Op3TYS0QODDhwTW/tP/BaZuyg56PnbKQ/XvDD+xpQ4kCggEAUZ/BVNK4TBjvAOFE
102 | w8KliNqc/Wh8rta9QOIGFej1gy53iLJCg9RxGRahFQH2GhCADgwXsTpFsNMwRLP8
103 | nCW59SDux4M/R3+T4GF76664G6Xqv7rgQBm8B7mQAfRd1Q3Eul8p757ilKTh1vtT
104 | 1V9Tp3zj7UIeyqMMkrXaw3LZrKB0TlIelLijTO9sskJURxejMGZuWShLfTgsWxkx
105 | 2hSlL3YcJHChQrEmEFFU7Y2EZtEH7qqQJmUvbWcVouqxKOqydvcNKmoYL3AxpFtr
106 | 7bsIQU4lV8U9ceKkPFP7ECKC8LLl3wS3tOqqxW1tRNMseeKQHFGiqnTSEbzSLm05
107 | O3vFKQKCAQAwfPu70rGlq2dXm1BIptst9dW8i5k6UHqBXRXFKORnmytH6J7JBbkT
108 | hWayosW4NJTp1zwep3V6gx4berSl+SWawm8R18r0qWRO6F5GGaxA7pTtgJc4j52e
109 | NX2Xm1xlEIv1tzh2WE7tehI+n1cL8ejCPYw7+HQxh7acHtkJzqynrn/xLhatoZdL
110 | d0A8M7mvyl+/KT+pDLtNCkbPX1emwXwIM78wE3l2Pv6qCUdD4QFiZHIkSCJul7A9
111 | PZOE9F3GC42+vYdeSqgBlp/8hkkgRIkx/lOfXKtBsMcr9WkIZaIOV/qkGeAavewy
112 | /FkY07K74lbUnXFqO/zUOi0dd/c4HOg5AoIBAGG43VP1Sc2WlNQiK4yr5GcVoR6p
113 | /BUIcOaKXxqPGR4OsZ7CxxXWBAldBCZo2lxvWV/9PiXSZ3VglM6dDtlRdKZp/CU9
114 | /WXM/TQ3HUi8yhG3p5y1LOvo3qQCvtlxbjZAlTKnwMIZ/mIjelv/hVk722ItsSdZ
115 | GBmZVkvKGO8kFr+RQgm5LVhmkn9PYMUdje2yMTP+Kuxa7R9sTmUQeIUMBnEtZ5R5
116 | NDpSpImXLJlcTszxPDdNIsAcrc+VQETi901khBMFxe7FzU7Fe0Nsf5wUkbo2BuhL
117 | yA5V366AarS/0vGFYUPb800cNsCqkBC6DeeKJ5+PbN+IsbCIwMOq9NSZJq0=
118 | -----END RSA PRIVATE KEY-----"""}
119 |
120 | response = self.client.post('/api/v1/core/sshkey/', data)
121 | self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
122 |
123 | response = self.client.get('/api/v1/core/sshkey/')
124 | self.assertEqual(response.status_code, status.HTTP_200_OK)
125 | self.assertEqual(response.data['count'], 2)
126 |
--------------------------------------------------------------------------------
/probemanager/core/tests/test_models.py:
--------------------------------------------------------------------------------
1 | """ venv/bin/python probemanager/manage.py test core.tests.test_models --settings=probemanager.settings.dev """
2 | from datetime import timedelta, datetime
3 |
4 | import pytz
5 | from django.db.utils import IntegrityError
6 | from django.test import TestCase
7 |
8 | from core.models import OsSupported, Probe, ProbeConfiguration, SshKey, Job, Server, Configuration
9 |
10 |
11 | class JobTest(TestCase):
12 | fixtures = ['init']
13 |
14 | @classmethod
15 | def setUpTestData(cls):
16 | now = datetime(year=2017, month=5, day=5, hour=12, tzinfo=pytz.UTC)
17 | before = now - timedelta(minutes=30)
18 | cls.job1 = Job.objects.create(name="test", probe="test", status='Error', result="",
19 | created=before, completed=now)
20 | cls.job2 = Job.create_job('test2', 'probe1')
21 |
22 | def test_job(self):
23 | self.assertEqual(str(self.job1), 'test')
24 | self.assertEqual(str(self.job2), 'test2')
25 | self.assertEqual(self.job2.status, 'In progress')
26 | self.job2.update_job('test model', 'Completed')
27 | self.assertEqual(self.job2.status, 'Completed')
28 | jobs = Job.get_all()
29 | self.assertTrue(jobs[0].created > jobs[1].created)
30 | for job in Job.get_all():
31 | job.delete()
32 | self.assertEqual(Job.get_last(), None)
33 |
34 |
35 | class OsSupportedTest(TestCase):
36 | fixtures = ['init']
37 | multi_db = False
38 |
39 | @classmethod
40 | def setUpTestData(cls):
41 | pass
42 |
43 | def test_os_supported(self):
44 | all_os_supported = OsSupported.get_all()
45 | os_supported = OsSupported.get_by_id(1)
46 | self.assertEqual(len(all_os_supported), 2)
47 | self.assertEqual(os_supported.name, "debian")
48 | self.assertEqual(str(os_supported), "debian")
49 | # Mixins
50 | self.assertEqual(OsSupported.get_last(), OsSupported.get_by_id(2))
51 | self.assertEqual(OsSupported.get_nbr(1)[0], os_supported)
52 | OsSupported.get_by_id(1).delete()
53 | OsSupported.get_by_id(2).delete()
54 | self.assertEqual(OsSupported.get_last(), None)
55 | os_supported = OsSupported.get_by_id(99)
56 | self.assertEqual(os_supported, None)
57 | with self.assertRaises(AttributeError):
58 | os_supported.name
59 | with self.assertLogs('core.models', level='DEBUG'):
60 | OsSupported.get_by_id(99)
61 | OsSupported.objects.create(name='debian')
62 | with self.assertRaises(IntegrityError):
63 | OsSupported.objects.create(name="debian")
64 |
65 |
66 | class SshKeyTest(TestCase):
67 | fixtures = ['init', 'crontab', 'test-core-sshkey']
68 |
69 | @classmethod
70 | def setUpTestData(cls):
71 | pass
72 |
73 | def test_sshKey(self):
74 | ssh_key = SshKey.objects.get(id=1)
75 | self.assertEqual(ssh_key.name, 'test')
76 | self.assertEqual(str(ssh_key), "test")
77 | with self.assertRaises(SshKey.DoesNotExist):
78 | SshKey.objects.get(id=199)
79 | with self.assertRaises(IntegrityError):
80 | SshKey.objects.create(name="test")
81 |
82 |
83 | class ServerTest(TestCase):
84 | fixtures = ['init', 'crontab', 'test-core-secrets']
85 |
86 | @classmethod
87 | def setUpTestData(cls):
88 | pass
89 |
90 | def test_server(self):
91 | self.assertEqual(str(Server.get_by_id(1)), ' - server.treussart.com, Os : debian')
92 | self.assertTrue(Server.get_by_id(1).test()['status'])
93 | self.assertTrue(Server.get_by_id(1).test_become()['status'])
94 | Server.objects.create(name="test",
95 | host="test",
96 | os=OsSupported.get_by_id(1),
97 | remote_user="test",
98 | remote_port=22,
99 | become=True,
100 | become_method="sudo",
101 | become_user="root",
102 | become_pass="not encrypted",
103 | ssh_private_key_file=SshKey.objects.get(id=1))
104 | self.assertNotEqual(Server.get_by_id(2).become_pass, "not encrypted")
105 | self.assertGreater(len(Server.get_by_id(2).become_pass), 99)
106 | self.assertEqual(str(Server.get_by_id(2)), 'test - test, Os : debian')
107 | self.assertFalse(Server.get_by_id(2).test()['status'])
108 | self.assertFalse(Server.get_by_id(2).test_become()['status'])
109 | self.assertEqual(Server.get_by_id(3), None)
110 |
111 |
112 | class ProbeTest(TestCase):
113 | fixtures = ['init', 'crontab', 'test-core-secrets', 'test-core-probe']
114 |
115 | @classmethod
116 | def setUpTestData(cls):
117 | pass
118 |
119 | def test_probe(self):
120 | all_probe = Probe.get_all()
121 | probe = Probe.get_by_id(1)
122 | self.assertEqual(Probe.get_by_name("probe1"), Probe.get_by_id(1))
123 | self.assertEqual(len(all_probe), 1)
124 | self.assertEqual(probe.name, "probe1")
125 | self.assertEqual(str(probe), "probe1")
126 | self.assertEqual(probe.description, "test")
127 | self.assertIn('Failed to get the uptime on the host :', probe.uptime())
128 | self.assertFalse(probe.start()['status'])
129 | self.assertFalse(probe.restart()['status'])
130 | self.assertFalse(probe.stop()['status'])
131 | self.assertFalse(probe.reload()['status'])
132 | self.assertEqual('Failed to get status', probe.status())
133 | probe.installed = False
134 | self.assertEqual('Not installed', probe.uptime())
135 | probe = Probe.get_by_id(99)
136 | self.assertEqual(probe, None)
137 | with self.assertRaises(AttributeError):
138 | probe.name
139 | probe = Probe.get_by_name("probe99")
140 | self.assertEqual(probe, None)
141 | with self.assertRaises(AttributeError):
142 | probe.name
143 | with self.assertLogs('core.models', level='DEBUG'):
144 | Probe.get_by_id(99)
145 | with self.assertLogs('core.models', level='DEBUG'):
146 | Probe.get_by_name('probe99')
147 | with self.assertRaises(IntegrityError):
148 | Probe.objects.create(name="suricata1")
149 |
150 |
151 | class ProbeConfigurationTest(TestCase):
152 | fixtures = ['init', 'test-core-probeconfiguration']
153 |
154 | @classmethod
155 | def setUpTestData(cls):
156 | pass
157 |
158 | def test_probe_configuration(self):
159 | all_probe_configuration = ProbeConfiguration.get_all()
160 | probe_configuration = ProbeConfiguration.get_by_id(1)
161 | self.assertEqual(len(all_probe_configuration), 1)
162 | self.assertEqual(probe_configuration.name, "conf1")
163 | self.assertEqual(str(probe_configuration), "conf1")
164 | probe_configuration = ProbeConfiguration.get_by_id(99)
165 | self.assertEqual(probe_configuration, None)
166 | with self.assertRaises(AttributeError):
167 | probe_configuration.name
168 | with self.assertLogs('core.models', level='DEBUG'):
169 | ProbeConfiguration.get_by_id(99)
170 | with self.assertRaises(IntegrityError):
171 | ProbeConfiguration.objects.create(name="conf1")
172 |
173 |
174 | class ConfigurationTest(TestCase):
175 | fixtures = ['init', 'crontab', 'test-core-probeconfiguration']
176 |
177 | @classmethod
178 | def setUpTestData(cls):
179 | pass
180 |
181 | def test_conf(self):
182 | conf = Configuration.objects.get(id=1)
183 | self.assertEqual(str(conf), "PUSHBULLET_API_KEY")
184 | self.assertEqual(conf.get_value('inexist'), None)
185 | self.assertEqual(conf.get_value("PUSHBULLET_API_KEY"), None)
186 | conf.value = "test"
187 | conf.save()
188 | self.assertEqual(conf.get_value("PUSHBULLET_API_KEY"), "test")
189 |
--------------------------------------------------------------------------------
/probemanager/core/tests/test_ssh.py:
--------------------------------------------------------------------------------
1 | """ venv/bin/python probemanager/manage.py test core.tests.test_ssh --settings=probemanager.settings.dev """
2 | from django.conf import settings
3 | from django.test import TestCase
4 |
5 | from core.models import Server
6 | from core.ssh import connection, execute, execute_copy
7 |
8 |
9 | class SshCoreTest(TestCase):
10 | fixtures = ['init', 'test-core-secrets']
11 |
12 | @classmethod
13 | def setUpTestData(cls):
14 | pass
15 |
16 | def test_connection(self):
17 | server = Server.get_by_id(1)
18 | client = connection(server)
19 | stdin, stdout, stderr = client.exec_command("hostname")
20 | self.assertEqual(stdout.channel.recv_exit_status(), 0)
21 | self.assertEqual(stdout.read().decode('utf-8'), 'test-travis\n')
22 | self.assertEqual(stderr.readlines(), [])
23 | client.close()
24 |
25 | def test_execute(self):
26 | server = Server.get_by_id(1)
27 | self.assertEqual(execute(server, {'test_hostame': "hostname"}, become=False),
28 | {'test_hostame': 'test-travis'})
29 | self.assertEqual(execute(server, {'test_ok': "hostname 1>/dev/null"}, become=False),
30 | {'test_ok': 'OK'})
31 | with self.assertRaises(Exception):
32 | execute(server, {'test_fail': "service ssh status"}, become=False)
33 |
34 | def test_execute_become(self):
35 | server = Server.get_by_id(1)
36 | self.assertEqual(execute(server, {'test_hostame': "hostname"}, become=True),
37 | {'test_hostame': 'test-travis'})
38 | self.assertIn('ssh.service', execute(server, {'test_status': "service ssh status"}, become=True)['test_status'])
39 | server.become_pass = None
40 | server.save()
41 | with self.assertRaises(Exception):
42 | execute(server, {'test_hostame': "hostname"}, become=True)
43 | server.become = False
44 | server.save()
45 | with self.assertRaises(Exception):
46 | execute(server, {'test_hostame': "hostname"}, become=True)
47 | with self.assertRaises(Exception):
48 | execute_copy(server, src=settings.ROOT_DIR + '/LICENSE', dest='/tmp/LICENSE', become=True)
49 |
50 | def test_execute_copy_put(self):
51 | server = Server.get_by_id(1)
52 | result = execute_copy(server, src=settings.ROOT_DIR + '/LICENSE', dest='LICENSE')
53 | self.assertEqual(result, {'copy': 'OK'})
54 | with self.assertRaises(Exception):
55 | execute_copy(server, src=settings.ROOT_DIR + '/LICENSE', dest='/')
56 |
57 | def test_execute_copy_put_become(self):
58 | server = Server.get_by_id(1)
59 | result = execute_copy(server, src=settings.ROOT_DIR + '/LICENSE', dest='/tmp/LICENSE', become=True)
60 | self.assertEqual(result, {'copy': 'OK', 'mv': {'mv': 'OK'}})
61 |
--------------------------------------------------------------------------------
/probemanager/core/tests/test_views.py:
--------------------------------------------------------------------------------
1 | """ venv/bin/python probemanager/manage.py test core.tests.test_views --settings=probemanager.settings.dev """
2 | from django.contrib.auth.models import User
3 | from django.test import Client, TestCase
4 |
5 | from core.models import Server
6 |
7 |
8 | class ViewsCoreTest(TestCase):
9 | fixtures = ['init', 'crontab', 'test-rules-source', 'test-core-secrets']
10 |
11 | def setUp(self):
12 | self.client = Client()
13 | User.objects.create_superuser(username='testuser', password='12345', email='testuser@test.com')
14 | if not self.client.login(username='testuser', password='12345'):
15 | self.assertRaises(Exception("Not logged"))
16 |
17 | def tearDown(self):
18 | self.client.logout()
19 |
20 | def test_login(self):
21 | """
22 | Django Admin login
23 | """
24 | response = self.client.get('/admin/login/', follow=True)
25 | self.assertEqual(response.status_code, 200)
26 | self.assertIn('Site administration | Probe Manager site admin', str(response.content))
27 | self.assertEqual('admin/index.html', response.templates[0].name)
28 | self.assertIn('admin', response.resolver_match.app_names)
29 | self.assertIn('function AdminSite.index', str(response.resolver_match.func))
30 |
31 | response = self.client.get('/admin/')
32 | self.assertEqual(response.status_code, 200)
33 | self.assertIn('Change', str(response.content))
34 | self.assertEqual(str(response.context['user']), 'testuser')
35 | with self.assertTemplateUsed('admin/index.html'):
36 | self.client.get('/admin/login/', follow=True)
37 |
38 | client_not_logged = Client()
39 | response = client_not_logged.get('/admin/login/', follow=True)
40 | self.assertEqual(response.status_code, 200)
41 | self.assertIn('Log in | Probe Manager site admin', str(response.content))
42 |
43 | response = client_not_logged.get('/admin/', follow=True)
44 | self.assertEqual(response.status_code, 200)
45 | self.assertIn('Log in | Probe Manager site admin', str(response.content))
46 | self.assertEqual(str(response.context['user']), 'AnonymousUser')
47 | response = client_not_logged.get('/admin/')
48 | self.assertRedirects(response, expected_url='/admin/login/?next=/admin/', status_code=302,
49 | target_status_code=200)
50 | with self.assertTemplateUsed('admin/login.html'):
51 | client_not_logged.get('/admin/', follow=True)
52 |
53 | def test_index(self):
54 | """
55 | Home page
56 | """
57 | response = self.client.get('/')
58 | self.assertEqual(response.status_code, 200)
59 | self.assertIn('Home', str(response.content))
60 | self.assertEqual('core/index.html', response.templates[0].name)
61 | self.assertIn('core', response.resolver_match.app_names)
62 | self.assertIn('function index', str(response.resolver_match.func))
63 | self.assertEqual(str(response.context['user']), 'testuser')
64 | with self.assertTemplateUsed('core/index.html'):
65 | self.client.get('/')
66 |
67 | def test_admin(self):
68 | response = self.client.get('/admin/core/server/', follow=True)
69 | self.assertEqual(response.status_code, 200)
70 | self.assertEqual(len(Server.get_all()), 1)
71 | response = self.client.post('/admin/core/server/add/', {'name': 'test-server',
72 | 'host': 'test.com',
73 | 'os': '1',
74 | 'remote_user': 'test',
75 | 'remote_port': 22,
76 | 'become_method': 'sudo',
77 | 'become_user': 'root',
78 | 'become_pass': 'test',
79 | 'become': True,
80 | 'ssh_private_key_file': '1',
81 | },
82 | follow=True)
83 | self.assertEqual(response.status_code, 200)
84 | self.assertIn(' was added successfully', str(response.content))
85 | self.assertIn('Connection to the server Failed', str(response.content))
86 | self.assertNotEqual(Server.objects.get(name='test-server').become_pass, 'test')
87 | self.assertEqual(len(Server.get_all()), 2)
88 | Server.get_by_id(1).delete()
89 | self.assertEqual(len(Server.get_all()), 1)
90 | response = self.client.post('/admin/core/server/add/', {'name': 'test-server-ok',
91 | 'host': 'server.treussart.com',
92 | 'os': '1',
93 | 'remote_user': 'travis',
94 | 'remote_port': 33003,
95 | 'become_method': 'sudo',
96 | 'become_user': 'root',
97 | 'become_pass': 'test',
98 | 'become': False,
99 | 'ssh_private_key_file': '1',
100 | },
101 | follow=True)
102 | self.assertEqual(response.status_code, 200)
103 | self.assertIn(' was added successfully', str(response.content))
104 | self.assertIn('Connection to the server OK', str(response.content))
105 | self.assertEqual(len(Server.get_all()), 2)
106 |
--------------------------------------------------------------------------------
/probemanager/core/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from . import views
4 |
5 | app_name = 'core'
6 | urlpatterns = [
7 | url(r'^$', views.index, name='index'),
8 | ]
9 |
--------------------------------------------------------------------------------
/probemanager/core/utils.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import os
4 | import shutil
5 | import subprocess
6 | import time
7 | from contextlib import contextmanager
8 |
9 | import psutil
10 | from cryptography.fernet import Fernet
11 | from django.conf import settings
12 | from django.db.utils import IntegrityError
13 | from django_celery_beat.models import PeriodicTask, CrontabSchedule
14 |
15 | fernet_key = Fernet(settings.FERNET_KEY)
16 | logger = logging.getLogger(__name__)
17 |
18 |
19 | def create_reload_task(probe):
20 | try:
21 | PeriodicTask.objects.get(name=probe.name + "_reload_task")
22 | except PeriodicTask.DoesNotExist:
23 | PeriodicTask.objects.create(crontab=probe.scheduled_rules_deployment_crontab,
24 | name=probe.name + "_reload_task",
25 | task='core.tasks.reload_probe',
26 | args=json.dumps([probe.name, ]))
27 |
28 |
29 | def create_check_task(probe):
30 | try:
31 | PeriodicTask.objects.get(name=probe.name + "_check_task")
32 | except PeriodicTask.DoesNotExist:
33 | if probe.scheduled_check_crontab:
34 | PeriodicTask.objects.create(crontab=probe.scheduled_check_crontab,
35 | name=probe.name + "_check_task",
36 | task='core.tasks.check_probe',
37 | enabled=probe.scheduled_check_enabled,
38 | args=json.dumps([probe.name, ]))
39 | else:
40 | PeriodicTask.objects.create(crontab=CrontabSchedule.objects.get(id=4),
41 | name=probe.name + "_check_task",
42 | task='core.tasks.check_probe',
43 | enabled=probe.scheduled_check_enabled,
44 | args=json.dumps([probe.name, ]))
45 |
46 |
47 | def create_deploy_rules_task(probe, schedule=None, source=None):
48 | if schedule is None:
49 | try:
50 | PeriodicTask.objects.get(
51 | name=probe.name + "_deploy_rules_" + str(probe.scheduled_rules_deployment_crontab))
52 | except PeriodicTask.DoesNotExist:
53 | try:
54 | if probe.scheduled_rules_deployment_crontab:
55 | PeriodicTask.objects.create(crontab=probe.scheduled_rules_deployment_crontab,
56 | name=probe.name + "_deploy_rules_" + str(
57 | probe.scheduled_rules_deployment_crontab),
58 | task='core.tasks.deploy_rules',
59 | enabled=probe.scheduled_rules_deployment_enabled,
60 | args=json.dumps([probe.name, ]))
61 | else:
62 | PeriodicTask.objects.create(crontab=CrontabSchedule.objects.get(id=4),
63 | name=probe.name + "_deploy_rules_" + str(
64 | CrontabSchedule.objects.get(id=4)),
65 | task='core.tasks.deploy_rules',
66 | enabled=probe.scheduled_rules_deployment_enabled,
67 | args=json.dumps([probe.name, ]))
68 | except IntegrityError:
69 | pass
70 | elif source is not None:
71 | try:
72 | PeriodicTask.objects.get(name=probe.name + "_" + source.uri + "_deploy_rules_" + str(schedule))
73 | except PeriodicTask.DoesNotExist:
74 | PeriodicTask.objects.create(crontab=schedule,
75 | name=probe.name + "_" + source.uri + "_deploy_rules_" + str(schedule),
76 | task='core.tasks.deploy_rules',
77 | enabled=probe.scheduled_rules_deployment_enabled,
78 | args=json.dumps([probe.name, ]))
79 |
80 |
81 | def decrypt(cipher_text):
82 | if isinstance(cipher_text, bytes):
83 | return fernet_key.decrypt(cipher_text)
84 | else:
85 | return fernet_key.decrypt(cipher_text.encode('utf-8')).decode('utf-8')
86 |
87 |
88 | def encrypt(plain_text):
89 | if isinstance(plain_text, bytes):
90 | return fernet_key.encrypt(plain_text)
91 | else:
92 | return fernet_key.encrypt(plain_text.encode('utf-8')).decode('utf-8')
93 |
94 |
95 | def add_10_min(crontab):
96 | schedule = crontab
97 | try:
98 | if schedule.minute == '*':
99 | # print("* -> 10")
100 | schedule.minute = '10'
101 | return schedule
102 | elif schedule.minute.isdigit():
103 | if int(schedule.minute) in range(0, 49):
104 | # print("0-50 -> +10")
105 | minute = int(schedule.minute)
106 | minute += 10
107 | schedule.minute = str(minute)
108 | return schedule
109 | elif schedule.hour.isdigit():
110 | hour = schedule.hour
111 | if int(hour) in range(0, 22):
112 | # print("50+ H0-22 -> H + 1 - 50")
113 | hour = int(schedule.hour)
114 | hour += 1
115 | schedule.hour = str(hour)
116 | minute = int(schedule.minute)
117 | schedule.minute = str(minute - 50)
118 | return schedule
119 | else:
120 | # print("50+ H23 -> ?")
121 | return schedule
122 | elif schedule.hour == '*':
123 | # print("50+ H* -> -50 +1H")
124 | minute = int(schedule.minute)
125 | schedule.minute = str(minute - 50)
126 | schedule.hour = '*/1'
127 | return schedule
128 | else:
129 | hour = int(schedule.hour[2:])
130 | # print("50+ H*/0+ -> +1h -50min")
131 | schedule.hour = '*/' + str(hour + 1)
132 | schedule.minute = str(int(schedule.minute) - 50)
133 | return schedule
134 | elif '/' in schedule.minute and int(schedule.minute[2:]) in range(10, 49):
135 | # print("*/0-49 -> +10min")
136 | minute = int(schedule.minute[2:])
137 | minute += 10
138 | schedule.minute = '*/' + str(minute)
139 | return schedule
140 | elif '/' in schedule.minute and int(schedule.minute[2:]) not in range(10, 49):
141 | if schedule.hour.isdigit():
142 | hour = int(schedule.hour)
143 | if hour in range(0, 22):
144 | # print("*/50+ H0-22 -> +1H -50min")
145 | hour += 1
146 | schedule.hour = str(hour)
147 | schedule.minute = '10'
148 | return schedule
149 | else:
150 | # print("*/50+ H23 -> ?")
151 | return schedule
152 | else: # pragma: no cover
153 | raise ValueError()
154 | except ValueError:
155 | return schedule
156 |
157 |
158 | def add_1_hour(crontab):
159 | schedule = crontab
160 | if schedule.minute.isdigit():
161 | if schedule.hour.isdigit():
162 | hour = schedule.hour
163 | if int(hour) in range(0, 22):
164 | # 2 1 -> 2 2
165 | hour = int(schedule.hour)
166 | hour += 1
167 | schedule.hour = str(hour)
168 | return schedule
169 | else:
170 | # 2 23 -> 2 0 + 1 jour
171 | if schedule.day_of_week.isdigit():
172 | schedule.hour = str(0)
173 | if int(schedule.day_of_week) in range(0, 5):
174 | schedule.day_of_week = str(int(schedule.day_of_week) + 1)
175 | else:
176 | schedule.day_of_week = str(0)
177 | else:
178 | # 2 23 * -> 2 0 + 1 jour illogic
179 | pass
180 | return schedule
181 | else:
182 | # 50 * -> equal illogic
183 | # 10 */2 -> equal illogic
184 | return schedule
185 | else:
186 | # */2 1 -> */2 2 illogic
187 | # */2 * -> equal illogic
188 | # * 23 -> * 0 + 1 jour illogic
189 | # * 1 -> * 2 illogic
190 | return schedule
191 |
192 |
193 | @contextmanager
194 | def get_tmp_dir(folder_name=None):
195 | if folder_name:
196 | tmp_dir = settings.BASE_DIR + '/tmp/' + str(folder_name) + '/' + str(time.time()) + '/'
197 | else:
198 | tmp_dir = settings.BASE_DIR + '/tmp/' + str(time.time()) + '/'
199 | try:
200 | if not os.path.exists(tmp_dir):
201 | os.makedirs(tmp_dir)
202 | yield tmp_dir
203 | finally:
204 | shutil.rmtree(tmp_dir, ignore_errors=True)
205 |
206 |
207 | def process_cmd(cmd, tmp_dir, value=None):
208 | try:
209 | process = subprocess.Popen(cmd, cwd=tmp_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
210 | universal_newlines=True)
211 | outdata, errdata = process.communicate()
212 | logger.debug("outdata : " + str(outdata), "errdata : " + str(errdata))
213 | except Exception as e:
214 | return {'status': False, 'errors': str(e)}
215 | else:
216 | if process.returncode != 0:
217 | return {'status': False, 'errors': outdata + errdata}
218 | elif value:
219 | if value in outdata or value in errdata:
220 | return {'status': False, 'errors': outdata + errdata}
221 | return {'status': True}
222 |
223 |
224 | def find_procs_by_name(name):
225 | """Return a list of processes matching 'name'."""
226 | ls = []
227 | for p in psutil.process_iter(attrs=["name", "exe", "cmdline"]):
228 | if name == p.info['name'] or \
229 | p.info['exe'] and os.path.basename(p.info['exe']) == name or \
230 | p.info['cmdline'] and p.info['cmdline'][0] == name:
231 | ls.append(p)
232 | return ls
233 |
--------------------------------------------------------------------------------
/probemanager/core/views.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | import logging
3 |
4 | from django.apps.registry import apps
5 | from django.contrib import messages
6 | from django.contrib.auth.decorators import login_required
7 | from django.http import HttpResponseNotFound
8 | from django.shortcuts import render
9 | from django.utils.safestring import mark_safe
10 |
11 | from .models import Probe
12 | from .tasks import deploy_rules as deploy_rules_probe, install_probe, update_probe
13 | from .utils import get_tmp_dir
14 |
15 | logger = logging.getLogger(__name__)
16 |
17 |
18 | @login_required
19 | def index(request):
20 | """
21 | Display all probes instances.
22 | """
23 | instances = dict()
24 | for app in apps.get_app_configs():
25 | for model in app.get_models():
26 | if issubclass(model, Probe):
27 | if app.verbose_name != "Core":
28 | my_class = getattr(importlib.import_module(app.label + ".models"), app.verbose_name)
29 | instances[app.label] = my_class.get_all()
30 | return render(request, 'core/index.html', {'instances': instances})
31 |
32 |
33 | @login_required
34 | def probe_index(request, pk):
35 | """
36 | Display an individual Probe instance.
37 | """
38 | probe = Probe.get_by_id(pk)
39 | if probe is None:
40 | return HttpResponseNotFound('Page not found
')
41 | if probe.subtype:
42 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
43 | else:
44 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
45 | probe = my_class.get_by_id(pk)
46 | if probe is None:
47 | return HttpResponseNotFound('Page not found
')
48 | else:
49 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
50 |
51 |
52 | @login_required
53 | def start(request, pk):
54 | """
55 | Start a probe instance.
56 | """
57 | probe = Probe.get_by_id(pk)
58 | if probe is None:
59 | return HttpResponseNotFound()
60 | if probe.subtype:
61 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
62 | else:
63 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
64 | probe = my_class.get_by_id(pk)
65 | try:
66 | response_start = probe.start()
67 | if response_start['status']:
68 | messages.add_message(request, messages.SUCCESS, 'Probe started successfully')
69 | else:
70 | messages.add_message(request, messages.ERROR,
71 | 'Error during the start: ' + str(response_start['errors']))
72 | except Exception as e:
73 | logger.exception('Error during the start : ' + str(e))
74 | messages.add_message(request, messages.ERROR, 'Error during the start : ' + str(e))
75 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
76 |
77 |
78 | @login_required
79 | def stop(request, pk):
80 | """
81 | Stop a probe instance.
82 | """
83 | probe = Probe.get_by_id(pk)
84 | if probe is None:
85 | return HttpResponseNotFound()
86 | if probe.subtype:
87 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
88 | else:
89 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
90 | probe = my_class.get_by_id(pk)
91 | try:
92 | response_stop = probe.stop()
93 | if response_stop['status']:
94 | messages.add_message(request, messages.SUCCESS, 'Probe stopped successfully')
95 | else:
96 | messages.add_message(request, messages.ERROR, 'Error during the stop: ' + str(response_stop['errors']))
97 | except Exception as e:
98 | logger.exception('Error during the stop : ' + str(e))
99 | messages.add_message(request, messages.ERROR, 'Error during the stop : ' + str(e))
100 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
101 |
102 |
103 | @login_required
104 | def restart(request, pk):
105 | """
106 | Restart a probe instance.
107 | """
108 | probe = Probe.get_by_id(pk)
109 | if probe is None:
110 | return HttpResponseNotFound()
111 | if probe.subtype:
112 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
113 | else:
114 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
115 | probe = my_class.get_by_id(pk)
116 | try:
117 | response_restart = probe.restart()
118 | if response_restart['status']:
119 | messages.add_message(request, messages.SUCCESS, 'Probe restarted successfully')
120 | else:
121 | messages.add_message(request, messages.ERROR,
122 | 'Error during the restart: ' + str(response_restart['errors']))
123 | except Exception as e:
124 | logger.exception('Error during the restart : ' + str(e))
125 | messages.add_message(request, messages.ERROR, 'Error during the restart : ' + str(e))
126 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
127 |
128 |
129 | @login_required
130 | def reload(request, pk):
131 | """
132 | Reload a probe instance.
133 | """
134 | probe = Probe.get_by_id(pk)
135 | if probe is None:
136 | return HttpResponseNotFound()
137 | if probe.subtype:
138 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
139 | else:
140 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
141 | probe = my_class.get_by_id(pk)
142 | try:
143 | response_reload = probe.reload()
144 | if response_reload['status']:
145 | messages.add_message(request, messages.SUCCESS, 'Probe reloaded successfully')
146 | else:
147 | messages.add_message(request, messages.ERROR,
148 | 'Error during the reload: ' + str(response_reload['errors']))
149 | except Exception as e:
150 | logger.exception('Error during the reload : ' + str(e))
151 | messages.add_message(request, messages.ERROR, 'Error during the reload : ' + str(e))
152 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
153 |
154 |
155 | @login_required
156 | def status(request, pk):
157 | """
158 | Status of a probe instance.
159 | """
160 | probe = Probe.get_by_id(pk)
161 | if probe is None:
162 | return HttpResponseNotFound()
163 | if probe.subtype:
164 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
165 | else:
166 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
167 | probe = my_class.get_by_id(pk)
168 | try:
169 | response_status = probe.status()
170 | if response_status:
171 | messages.add_message(request, messages.SUCCESS,
172 | "OK probe " + str(probe.name) + " get status successfully")
173 | else:
174 | messages.add_message(request, messages.ERROR, 'Error during the status')
175 | except Exception as e:
176 | logger.exception('Error during the status : ' + str(e))
177 | messages.add_message(request, messages.ERROR, 'Error during the status : ' + str(e))
178 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
179 |
180 |
181 | @login_required
182 | def install(request, pk):
183 | """
184 | Install a probe instance.
185 | """
186 | probe = Probe.get_by_id(pk)
187 | if probe is None:
188 | return HttpResponseNotFound()
189 | if probe.subtype:
190 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
191 | else:
192 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
193 | probe = my_class.get_by_id(pk)
194 | try:
195 | install_probe.delay(probe.name)
196 | except Exception as e:
197 | logger.exception('Error during the install : ' + str(e))
198 | messages.add_message(request, messages.ERROR, 'Error during the install : ' + str(e))
199 | messages.add_message(request, messages.SUCCESS, mark_safe("Install probe launched with succeed. " +
200 | "View Job"))
201 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
202 |
203 |
204 | @login_required
205 | def update(request, pk):
206 | """
207 | Update a probe instance.
208 | """
209 | probe = Probe.get_by_id(pk)
210 | if probe is None:
211 | return HttpResponseNotFound()
212 | if probe.subtype:
213 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
214 | else:
215 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
216 | probe = my_class.get_by_id(pk)
217 | try:
218 | update_probe.delay(probe.name)
219 | except Exception as e:
220 | logger.exception('Error during the update : ' + str(e))
221 | messages.add_message(request, messages.ERROR, 'Error during the update : ' + str(e))
222 | messages.add_message(request, messages.SUCCESS, mark_safe("Update probe launched with succeed. " +
223 | "View Job"))
224 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
225 |
226 |
227 | @login_required
228 | def deploy_conf(request, pk):
229 | """
230 | Deploy the configuration of a probe instance.
231 | """
232 | probe = Probe.get_by_id(pk)
233 | if probe is None:
234 | return HttpResponseNotFound()
235 | if probe.subtype:
236 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
237 | else:
238 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
239 | probe = my_class.get_by_id(pk)
240 | response_test = probe.configuration.test()
241 | logger.debug(str(response_test))
242 | if probe.secure_deployment:
243 | if not response_test['status']:
244 | messages.add_message(request, messages.ERROR, 'Error during the test configuration')
245 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
246 | if response_test['status']:
247 | messages.add_message(request, messages.SUCCESS, "Test configuration OK")
248 | else:
249 | messages.add_message(request, messages.ERROR, "Test configuration failed ! " + str(response_test['errors']))
250 | try:
251 | response_deploy_conf = probe.deploy_conf()
252 | response_restart = probe.restart()
253 | if response_deploy_conf['status'] and response_restart['status']:
254 | messages.add_message(request, messages.SUCCESS, 'Deployed configuration successfully')
255 | elif not response_deploy_conf['status']:
256 | messages.add_message(request, messages.ERROR,
257 | 'Error during the configuration deployed: ' + str(response_deploy_conf['errors']))
258 | elif not response_restart['status']:
259 | messages.add_message(request, messages.ERROR,
260 | 'Error during the configuration deployed: ' + str(response_restart['errors']))
261 | except Exception as e:
262 | logger.exception('Error during the configuration deployed : ' + str(e))
263 | messages.add_message(request, messages.ERROR, 'Error during the configuration deployed : ' + str(e))
264 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
265 |
266 |
267 | @login_required
268 | def deploy_rules(request, pk):
269 | """
270 | Deploy the rules of a probe instance.
271 | """
272 | probe = Probe.get_by_id(pk)
273 | if probe is None:
274 | return HttpResponseNotFound()
275 | if probe.subtype:
276 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.subtype)
277 | else:
278 | my_class = getattr(importlib.import_module(probe.type.lower() + ".models"), probe.type)
279 | probe = my_class.get_by_id(pk)
280 | try:
281 | deploy_rules_probe.delay(probe.name)
282 | messages.add_message(request, messages.SUCCESS, mark_safe(
283 | "Deployed rules launched with succeed. " +
284 | "View Job"))
285 | except Exception as e:
286 | logger.exception('Error during the rules deployment : ' + str(e))
287 | messages.add_message(request, messages.ERROR, 'Error during the rules deployment : ' + str(e))
288 | return render(request, probe.type.lower() + '/index.html', {'probe': probe})
289 |
290 |
291 | def generic_import_csv(cls, request):
292 | if request.method == 'GET':
293 | return render(request, 'import_csv.html')
294 | elif request.method == 'POST':
295 | if request.FILES['file']:
296 | try:
297 | with get_tmp_dir('csv') as tmp_dir:
298 | with open(tmp_dir + 'imported.csv', 'wb+') as destination:
299 | for chunk in request.FILES['file'].chunks():
300 | destination.write(chunk)
301 | cls.import_from_csv(tmp_dir + 'imported.csv')
302 | except Exception as e:
303 | messages.add_message(request, messages.ERROR, 'Error during the import : ' + str(e))
304 | return render(request, 'import_csv.html')
305 | messages.add_message(request, messages.SUCCESS, 'CSV file imported successfully !')
306 | return render(request, 'import_csv.html')
307 | else: # pragma: no cover
308 | messages.add_message(request, messages.ERROR, 'No file submitted')
309 | return render(request, 'import_csv.html')
310 |
--------------------------------------------------------------------------------
/probemanager/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "probemanager.settings.dev")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/probemanager/probemanager/__init__.py:
--------------------------------------------------------------------------------
1 | from .celery import app as celery_app
2 | import platform
3 | import sys
4 | import warnings
5 |
6 |
7 | if not (3) <= sys.version_info[0]:
8 | sys.exit(
9 | 'ERROR: Probe Manager requires Python 3, but found %s.' %
10 | platform.python_version())
11 |
12 | warnings.filterwarnings('ignore', 'could not open display')
13 |
14 |
15 | __all__ = ['celery_app']
16 | __author__ = 'TREUSSART Matthieu'
17 | __author_email__ = 'matthieu@treussart.com'
18 | __title__ = 'ProbeManager'
19 | __licence__ = 'GNU GPLv3'
20 |
--------------------------------------------------------------------------------
/probemanager/probemanager/celery.py:
--------------------------------------------------------------------------------
1 | from celery import Celery
2 |
3 |
4 | app = Celery('probemanager')
5 |
6 | # Using a string here means the worker doesn't have to serialize
7 | # the configuration object to child processes.
8 | # - namespace='CELERY' means all celery-related configuration keys
9 | # should have a `CELERY_` prefix.
10 | app.config_from_object('django.conf:settings', namespace='CELERY')
11 |
12 | # Load task modules from all registered Django app configs.
13 | app.autodiscover_tasks()
14 |
--------------------------------------------------------------------------------
/probemanager/probemanager/fixtures/crontab.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "django_celery_beat.crontabschedule",
4 | "pk": 1,
5 | "fields": {
6 | "minute": "*/30",
7 | "hour": "*",
8 | "day_of_week": "*",
9 | "day_of_month": "*",
10 | "month_of_year": "*"
11 | }
12 | },
13 | {
14 | "model": "django_celery_beat.crontabschedule",
15 | "pk": 2,
16 | "fields": {
17 | "minute": "0",
18 | "hour": "*/1",
19 | "day_of_week": "*",
20 | "day_of_month": "*",
21 | "month_of_year": "*"
22 | }
23 | },
24 | {
25 | "model": "django_celery_beat.crontabschedule",
26 | "pk": 3,
27 | "fields": {
28 | "minute": "0",
29 | "hour": "4",
30 | "day_of_week": "0",
31 | "day_of_month": "*",
32 | "month_of_year": "*"
33 | }
34 | },
35 | {
36 | "model": "django_celery_beat.crontabschedule",
37 | "pk": 4,
38 | "fields": {
39 | "minute": "0",
40 | "hour": "4",
41 | "day_of_week": "*",
42 | "day_of_month": "*",
43 | "month_of_year": "*"
44 | }
45 | }
46 | ]
47 |
--------------------------------------------------------------------------------
/probemanager/probemanager/fixtures/init.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "core.ossupported",
4 | "pk": 1,
5 | "fields": {
6 | "name": "debian"
7 | }
8 | },
9 | {
10 | "model": "core.ossupported",
11 | "pk": 2,
12 | "fields": {
13 | "name": "ubuntu"
14 | }
15 | },
16 | {
17 | "model": "core.configuration",
18 | "pk": 1,
19 | "fields": {
20 | "key": "PUSHBULLET_API_KEY",
21 | "value": ""
22 | }
23 | },
24 | {
25 | "model": "core.configuration",
26 | "pk": 2,
27 | "fields": {
28 | "key": "SPLUNK_HOST",
29 | "value": ""
30 | }
31 | },
32 | {
33 | "model": "core.configuration",
34 | "pk": 3,
35 | "fields": {
36 | "key": "SPLUNK_USER",
37 | "value": ""
38 | }
39 | },
40 | {
41 | "model": "core.configuration",
42 | "pk": 4,
43 | "fields": {
44 | "key": "SPLUNK_PASSWORD",
45 | "value": ""
46 | }
47 | },
48 | {
49 | "model": "core.configuration",
50 | "pk": 5,
51 | "fields": {
52 | "key": "MISP_HOST",
53 | "value": ""
54 | }
55 | },
56 | {
57 | "model": "core.configuration",
58 | "pk": 6,
59 | "fields": {
60 | "key": "MISP_API_KEY",
61 | "value": ""
62 | }
63 | },
64 | {
65 | "model": "rules.datatypeupload",
66 | "pk": 0,
67 | "fields": {
68 | "name": "multiple files in compressed file"
69 | }
70 | },
71 | {
72 | "model": "rules.datatypeupload",
73 | "pk": 1,
74 | "fields": {
75 | "name": "one file not compressed"
76 | }
77 | },
78 | {
79 | "model": "rules.methodupload",
80 | "pk": 0,
81 | "fields": {
82 | "name": "URL HTTP"
83 | }
84 | },
85 | {
86 | "model": "rules.methodupload",
87 | "pk": 1,
88 | "fields": {
89 | "name": "Upload file"
90 | }
91 | },
92 | {
93 | "model": "rules.methodupload",
94 | "pk": 2,
95 | "fields": {
96 | "name": "MISP"
97 | }
98 | }
99 | ]
100 |
--------------------------------------------------------------------------------
/probemanager/probemanager/settings/__init__.py:
--------------------------------------------------------------------------------
1 | from probemanager.settings.base import *
2 |
--------------------------------------------------------------------------------
/probemanager/probemanager/settings/base.py:
--------------------------------------------------------------------------------
1 | import os
2 | import configparser
3 |
4 |
5 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
6 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
7 |
8 | ROOT_DIR = os.path.abspath(os.path.join(BASE_DIR, os.pardir))
9 |
10 | config = configparser.ConfigParser()
11 | config.read(os.path.join(ROOT_DIR, 'conf.ini'))
12 |
13 | MEDIA_ROOT = BASE_DIR
14 | FILE_UPLOAD_PERMISSIONS = 0o640
15 |
16 | BASE_APPS = [
17 | 'django.contrib.admin',
18 | 'django.contrib.auth',
19 | 'django.contrib.contenttypes',
20 | 'django.contrib.sessions',
21 | 'django.contrib.messages',
22 | 'django.contrib.staticfiles',
23 | 'django_celery_beat',
24 | 'django_extensions',
25 | 'rest_framework',
26 | 'rest_framework.authtoken',
27 | 'rest_framework_swagger',
28 | 'select2',
29 | 'api',
30 | 'rules',
31 | 'core',
32 | ]
33 |
34 | MIDDLEWARE = [
35 | 'django.middleware.security.SecurityMiddleware',
36 | 'django.contrib.sessions.middleware.SessionMiddleware',
37 | 'django.middleware.common.CommonMiddleware',
38 | 'django.middleware.csrf.CsrfViewMiddleware',
39 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
40 | 'django.contrib.messages.middleware.MessageMiddleware',
41 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
42 | 'django.contrib.admindocs.middleware.XViewMiddleware',
43 | ]
44 |
45 | ROOT_URLCONF = 'probemanager.urls'
46 |
47 | TEMPLATES = [
48 | {
49 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
50 | 'DIRS': [os.path.join(BASE_DIR, 'templates')],
51 | 'APP_DIRS': True,
52 | 'OPTIONS': {
53 | 'context_processors': [
54 | 'django.template.context_processors.debug',
55 | 'django.template.context_processors.request',
56 | 'django.contrib.auth.context_processors.auth',
57 | 'django.contrib.messages.context_processors.messages',
58 | ],
59 | },
60 | },
61 | ]
62 |
63 | WSGI_APPLICATION = 'probemanager.wsgi.application'
64 |
65 | # Password validation
66 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
67 |
68 | AUTH_PASSWORD_VALIDATORS = [
69 | {
70 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
71 | },
72 | {
73 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
74 | },
75 | {
76 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
77 | },
78 | {
79 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
80 | },
81 | ]
82 |
83 |
84 | # Internationalization
85 | # https://docs.djangoproject.com/en/1.11/topics/i18n/
86 |
87 | LANGUAGE_CODE = 'en-us'
88 |
89 | USE_I18N = True
90 |
91 | USE_L10N = True
92 |
93 | USE_TZ = True
94 |
95 |
96 | # Static files (CSS, JavaScript, Images)
97 | # https://docs.djangoproject.com/en/1.11/howto/static-files/
98 | STATIC_URL = '/static/'
99 |
100 | LOGIN_URL = '/admin/login/'
101 |
102 | #: Only add pickle to this list if your broker is secured
103 | #: from unwanted access (see userguide/security.html)
104 | CELERY_ACCEPT_CONTENT = ['json']
105 | CELERY_TASK_SERIALIZER = 'json'
106 |
107 | # Security
108 | CSRF_COOKIE_HTTPONLY = True
109 | SECURE_BROWSER_XSS_FILTER = True
110 | SECURE_CONTENT_TYPE_NOSNIFF = True
111 | X_FRAME_OPTIONS = 'DENY'
112 |
113 | DATA_UPLOAD_MAX_NUMBER_FIELDS = None
114 |
115 | PASSWORD_HASHERS = [
116 | 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
117 | ]
118 |
119 | REST_FRAMEWORK = {
120 | 'DEFAULT_PAGINATION_CLASS': 'api.pagination.StandardResultsSetPagination',
121 | 'DEFAULT_PERMISSION_CLASSES': [
122 | 'rest_framework.permissions.IsAdminUser',
123 | ],
124 | 'TEST_REQUEST_DEFAULT_FORMAT': 'json',
125 | 'DEFAULT_AUTHENTICATION_CLASSES': (
126 | 'rest_framework.authentication.TokenAuthentication',
127 | 'rest_framework.authentication.SessionAuthentication',
128 | )
129 | }
130 |
131 | FIXTURE_DIRS = [BASE_DIR + '/probemanager/fixtures', ]
132 |
133 |
134 | LOGGING = {
135 | 'version': 1,
136 | 'disable_existing_loggers': False,
137 | 'filters': {
138 | 'require_debug_true': {
139 | '()': 'django.utils.log.RequireDebugTrue'
140 | },
141 | 'require_debug_false': {
142 | '()': 'django.utils.log.RequireDebugFalse'
143 | },
144 | },
145 | 'formatters': {
146 | 'full': {
147 | 'format': '%(asctime)s %(levelname)s - %(name)s.%(funcName)s %(filename)s:%(lineno)d - %(message)s',
148 | 'datefmt': '%Y-%m-%d %H:%M:%S'
149 | },
150 | 'simple': {
151 | 'format': '%(levelname)s %(message)s'
152 | },
153 | },
154 | 'handlers': {
155 | 'mail_admins': {
156 | 'level': 'ERROR',
157 | 'filters': ['require_debug_false'],
158 | 'class': 'django.utils.log.AdminEmailHandler',
159 | 'include_html': True
160 | },
161 | 'file': {
162 | 'level': 'INFO',
163 | 'class': 'logging.handlers.RotatingFileHandler',
164 | 'formatter': 'full',
165 | 'filters': ['require_debug_false']
166 | },
167 | 'file-error': {
168 | 'level': 'ERROR',
169 | 'class': 'logging.handlers.RotatingFileHandler',
170 | 'formatter': 'full',
171 | 'filters': ['require_debug_false']
172 | },
173 | 'console': {
174 | 'level': 'DEBUG',
175 | 'class': 'logging.StreamHandler',
176 | 'formatter': 'full',
177 | 'filters': ['require_debug_true']
178 | },
179 | },
180 | 'loggers': {
181 | 'django': {
182 | 'handlers': ['console', 'file', 'file-error'],
183 | 'level': 'DEBUG',
184 | },
185 | 'django.template': {
186 | 'handlers': ['console', 'file', 'file-error'],
187 | 'level': 'INFO',
188 | 'propagate': True,
189 | },
190 | 'django.security': {
191 | 'handlers': ['console', 'file', 'file-error'],
192 | 'level': 'DEBUG',
193 | 'propagate': True
194 | },
195 | 'django.db.backends': {
196 | 'handlers': ['console', 'file', 'file-error'],
197 | 'level': 'INFO',
198 | 'propagate': True
199 | },
200 | 'core': {
201 | 'handlers': ['console', 'file', 'file-error'],
202 | 'propagate': True
203 | },
204 | 'rules': {
205 | 'handlers': ['console', 'file', 'file-error'],
206 | 'propagate': True
207 | },
208 | },
209 |
210 | }
211 |
212 | SWAGGER_SETTINGS = {
213 | "VALIDATOR_URL": False,
214 | }
215 |
216 | if os.path.isfile(os.path.join(BASE_DIR, 'core/fixtures/test-core-secrets.ini')):
217 | config_secrets = configparser.ConfigParser()
218 | config_secrets.read(os.path.join(BASE_DIR, 'core/fixtures/test-core-secrets.ini'))
219 | EMAIL_HOST = config_secrets['EMAIL']['EMAIL_HOST']
220 | EMAIL_PORT = int(config_secrets['EMAIL']['EMAIL_PORT'])
221 | EMAIL_HOST_USER = config_secrets['EMAIL']['EMAIL_HOST_USER']
222 | DEFAULT_FROM_EMAIL = config_secrets['EMAIL']['DEFAULT_FROM_EMAIL']
223 | EMAIL_USE_TLS = config_secrets.getboolean('EMAIL', 'EMAIL_USE_TLS')
224 | EMAIL_HOST_PASSWORD = config_secrets['EMAIL']['EMAIL_HOST_PASSWORD']
225 |
--------------------------------------------------------------------------------
/probemanager/probemanager/settings/dev.py:
--------------------------------------------------------------------------------
1 | from probemanager.settings.base import * # noqa
2 | from core.git import git_tag
3 | import configparser
4 | import os
5 |
6 |
7 | config = configparser.ConfigParser()
8 | config.read(os.path.join(ROOT_DIR, 'conf.ini'))
9 |
10 | # SECURITY WARNING: don't run with debug turned on in production!
11 | DEBUG = True
12 | ALLOWED_HOSTS = ['*']
13 |
14 | SECRET_KEY = 'j-93#)n%t8d0%tyo$f2e+$!%5-#wj65d#85@9y8jf)%_69_1ek'
15 | FERNET_KEY = b'ly8WTzGyN6Xz23t5yq_s_1Ob-qmccqdi52Baj4ta_qQ='
16 |
17 | GIT_BINARY = config['GIT']['GIT_BINARY']
18 |
19 | VERSION = git_tag(ROOT_DIR)
20 |
21 | # Celery settings
22 | CELERY_BROKER_URL = 'amqp://guest:guest@localhost//'
23 |
24 | TIME_ZONE = 'UTC'
25 |
26 | CACHES = {
27 | 'default': {
28 | 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
29 | }
30 | }
31 |
32 | DATABASES = {
33 | 'default': {
34 | 'ENGINE': 'django.db.backends.postgresql',
35 | 'NAME': 'probemanager',
36 | 'USER': 'probemanager',
37 | 'PASSWORD': 'probemanager',
38 | 'HOST': '127.0.0.1',
39 | 'PORT': '5432',
40 | }
41 | }
42 |
43 | SPECIFIC_APPS = ['suricata', 'checkcve', 'bro']
44 | INSTALLED_APPS = BASE_APPS + SPECIFIC_APPS
45 |
46 | for app in SPECIFIC_APPS:
47 | LOGGING['loggers'].update({app: {'handlers': ['console'], 'propagate': True}})
48 | if os.path.isfile(BASE_DIR + "/" + app + "/settings.py"):
49 | exec(open(BASE_DIR + "/" + app + "/settings.py").read())
50 |
51 | LOGGING['handlers']['file'].update({'filename': os.path.join(BASE_DIR, 'probemanager.log')})
52 | LOGGING['handlers']['file-error'].update({'filename': os.path.join(BASE_DIR, 'probemanager-error.log')})
53 |
54 | TEMPLATES[0]['OPTIONS']['debug'] = True
55 |
--------------------------------------------------------------------------------
/probemanager/probemanager/settings/prod.py:
--------------------------------------------------------------------------------
1 | from probemanager.settings.base import * # noqa
2 | from cryptography.fernet import Fernet
3 | import configparser
4 | import ast
5 | import os
6 |
7 |
8 | config = configparser.ConfigParser()
9 | config.read(os.path.join(ROOT_DIR, 'conf.ini'))
10 |
11 | # SECURITY WARNING: don't run with debug turned on in production!
12 | DEBUG = False
13 |
14 | HOST = config['DEFAULT']['HOST']
15 | ALLOWED_HOSTS = [HOST, 'localhost', '127.0.0.1']
16 | GIT_BINARY = config['GIT']['GIT_BINARY']
17 |
18 | # Specific for installation
19 | PROJECT_NAME = 'probemanager'
20 | APACHE_PORT = 80
21 |
22 | with open(os.path.join(ROOT_DIR, 'secret_key.txt'), encoding='utf_8') as f:
23 | SECRET_KEY = f.read().strip()
24 | with open(os.path.join(ROOT_DIR, 'fernet_key.txt'), encoding='utf_8') as f:
25 | FERNET_KEY = bytes(f.read().strip(), 'utf-8')
26 |
27 | if os.path.isfile(os.path.join(BASE_DIR, 'version.txt')):
28 | with open(os.path.join(BASE_DIR, 'version.txt'), encoding='utf_8') as f:
29 | VERSION = f.read().strip()
30 | else:
31 | VERSION = ""
32 |
33 |
34 | def decrypt(cipher_text):
35 | fernet_key = Fernet(FERNET_KEY)
36 | if isinstance(cipher_text, bytes):
37 | return fernet_key.decrypt(cipher_text).decode('utf-8')
38 | else:
39 | return fernet_key.decrypt(cipher_text.encode('utf-8')).decode('utf-8')
40 |
41 |
42 | if os.path.isfile(os.path.join(ROOT_DIR, 'password_db.txt')):
43 | with open(os.path.join(ROOT_DIR, 'password_db.txt'), encoding='utf_8') as f:
44 | PASSWORD_DB = decrypt(f.read().strip())
45 | else:
46 | PASSWORD_DB = "probemanager"
47 |
48 | # Celery settings
49 | CELERY_BROKER_URL = 'amqp://guest:guest@localhost//'
50 |
51 | STATIC_ROOT = os.path.join(BASE_DIR, 'static')
52 |
53 | DATABASES = {
54 | 'default': {
55 | 'ENGINE': 'django.db.backends.postgresql',
56 | 'NAME': 'probemanager',
57 | 'USER': 'probemanager',
58 | 'PASSWORD': PASSWORD_DB,
59 | 'HOST': '127.0.0.1',
60 | 'PORT': '5432',
61 | }
62 | }
63 |
64 | SPECIFIC_APPS = ast.literal_eval(config['APPS']['PROD_APPS'])
65 | INSTALLED_APPS = BASE_APPS + SPECIFIC_APPS
66 |
67 | for app in SPECIFIC_APPS:
68 | LOGGING['loggers'].update({app: {'handlers': ['file', 'file-error'], 'propagate': True}})
69 | if os.path.isfile(BASE_DIR + "/" + app + "/settings.py"):
70 | exec(open(BASE_DIR + "/" + app + "/settings.py", encoding='utf_8').read())
71 |
72 | LOGGING['handlers']['file'].update({'filename': config['LOG']['FILE_PATH']})
73 | LOGGING['handlers']['file-error'].update({'filename': config['LOG']['FILE_ERROR_PATH']})
74 |
75 | TIME_ZONE = config['DEFAULT']['TIME_ZONE']
76 |
77 | if not os.path.isfile(os.path.join(BASE_DIR, 'core/fixtures/test-core-secrets.ini')):
78 | EMAIL_HOST = config['EMAIL']['EMAIL_HOST']
79 | EMAIL_PORT = int(config['EMAIL']['EMAIL_PORT'])
80 | EMAIL_HOST_USER = config['EMAIL']['EMAIL_HOST_USER']
81 | DEFAULT_FROM_EMAIL = config['EMAIL']['DEFAULT_FROM_EMAIL']
82 | EMAIL_USE_TLS = config.getboolean('EMAIL', 'EMAIL_USE_TLS')
83 | if os.path.isfile(os.path.join(ROOT_DIR, 'password_email.txt')):
84 | with open(os.path.join(ROOT_DIR, 'password_email.txt'), encoding='utf_8') as f:
85 | EMAIL_HOST_PASSWORD = decrypt(f.read().strip())
86 | elif config.has_option('EMAIL','EMAIL_HOST_PASSWORD'):
87 | EMAIL_HOST_PASSWORD = config['EMAIL']['EMAIL_HOST_PASSWORD']
88 | else:
89 | EMAIL_HOST_PASSWORD = ""
90 |
--------------------------------------------------------------------------------
/probemanager/probemanager/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url, include
2 | from django.contrib import admin
3 | from rest_framework_swagger.views import get_swagger_view
4 | from django.conf import settings
5 |
6 |
7 | schema_view = get_swagger_view(title='ProbeManager API', url='/')
8 |
9 | urlpatterns_modules = list()
10 | for app in settings.SPECIFIC_APPS:
11 | urlpatterns_modules.append(url(r'^' + app + '/', include(app + '.urls')))
12 |
13 | urlpatterns = [
14 | url(r'^api/v1/doc/$', schema_view),
15 | url(r'^api/v1/', include('api.urls')),
16 | url(r'^admin/', admin.site.urls),
17 | url(r'^select2/', include('select2.urls')),
18 | url(r'^rules/', include('rules.urls')),
19 | url(r'^', include('core.urls')),
20 | ]
21 | urlpatterns += urlpatterns_modules
22 |
--------------------------------------------------------------------------------
/probemanager/probemanager/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for probemanager project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "probemanager.settings.prod")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/probemanager/rules/README.rst:
--------------------------------------------------------------------------------
1 | *****
2 | Rules
3 | *****
4 |
5 | Presentation
6 | ============
7 |
8 | Application with generic things about rules.
9 |
10 | Features
11 | --------
12 |
13 | * DataTypeUpload
14 | * MethodUpload
15 | * Generic Rule
16 | * Generic RuleSet
17 | * Generic Source
18 |
--------------------------------------------------------------------------------
/probemanager/rules/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/rules/__init__.py
--------------------------------------------------------------------------------
/probemanager/rules/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class RulesConfig(AppConfig):
5 | name = 'rules'
6 |
--------------------------------------------------------------------------------
/probemanager/rules/fixtures/test-rules-rule.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "rules.rule",
4 | "pk": 1,
5 | "fields": {
6 | "rev": 0,
7 | "reference": "http://www.exemple.com",
8 | "rule_full": "test",
9 | "enabled": true,
10 | "created_date": "2017-09-23T21:07:36.840Z",
11 | "updated_date": "2017-09-23T21:07:36.840Z"
12 | }
13 | }
14 | ]
15 |
--------------------------------------------------------------------------------
/probemanager/rules/fixtures/test-rules-ruleset.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "rules.ruleset",
4 | "pk": 1,
5 | "fields": {
6 | "name": "ruleset1",
7 | "description": "test",
8 | "created_date": "2017-09-23T21:09:11.291Z",
9 | "updated_date": null
10 | }
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/probemanager/rules/fixtures/test-rules-source.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "rules.source",
4 | "pk": 1,
5 | "fields": {
6 | "method": 0,
7 | "data_type": 1,
8 | "uri": "https://sslbl.abuse.ch/blacklist/sslblacklist.rules",
9 | "scheduled_rules_deployment_enabled": true,
10 | "scheduled_rules_deployment_crontab": 1,
11 | "scheduled_deploy": true,
12 | "file": "",
13 | "type": "SourceSuricata"
14 | }
15 | },
16 | {
17 | "model": "rules.source",
18 | "pk": 2,
19 | "fields": {
20 | "method": 0,
21 | "data_type": 0,
22 | "uri": "https://rules.emergingthreats.net/open/suricata-3.3.1/emerging.rules.tar.gz",
23 | "scheduled_rules_deployment_enabled": true,
24 | "scheduled_rules_deployment_crontab": 1,
25 | "scheduled_deploy": true,
26 | "file": "",
27 | "type": "SourceSuricata"
28 | }
29 | }
30 | ]
31 |
--------------------------------------------------------------------------------
/probemanager/rules/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/rules/migrations/__init__.py
--------------------------------------------------------------------------------
/probemanager/rules/models.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import time
3 |
4 | from django.db import models
5 | from django.utils import timezone
6 | from django_celery_beat.models import CrontabSchedule
7 |
8 | from core.modelsmixins import CommonMixin
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 |
13 | class Rule(CommonMixin, models.Model):
14 | """
15 | Represent a rule, who can be a script or a signature.
16 | """
17 | id = models.AutoField(primary_key=True)
18 | rev = models.IntegerField(default=0)
19 | reference = models.CharField(max_length=1000, blank=True, null=True)
20 | rule_full = models.TextField(max_length=10000)
21 | enabled = models.BooleanField(default=True)
22 | created_date = models.DateTimeField(default=timezone.now, editable=False)
23 | updated_date = models.DateTimeField(default=timezone.now, editable=False)
24 |
25 | @classmethod
26 | def find(cls, pattern):
27 | """Search the pattern in all the scripts and signatures"""
28 | return cls.objects.filter(rule_full__icontains=pattern)
29 |
30 |
31 | class DataTypeUpload(CommonMixin, models.Model):
32 | """
33 | Data type, for differentiate the uploaded file (compressed, uncompressed, multiple files, one file).
34 | """
35 | name = models.CharField(max_length=100, unique=True, blank=False, null=False)
36 |
37 | def __str__(self):
38 | return self.name
39 |
40 | @classmethod
41 | def get_by_name(cls, name):
42 | try:
43 | obj = cls.objects.get(name=name)
44 | except cls.DoesNotExist as e:
45 | logger.debug('Tries to access an object that does not exist : ' + str(e))
46 | return None
47 | return obj
48 |
49 |
50 | class MethodUpload(CommonMixin, models.Model):
51 | """
52 | Method use for the upload. By URL, File
53 | """
54 | name = models.CharField(max_length=100, unique=True, blank=False, null=False)
55 |
56 | def __str__(self):
57 | return self.name
58 |
59 | @classmethod
60 | def get_by_name(cls, name):
61 | try:
62 | obj = cls.objects.get(name=name)
63 | except cls.DoesNotExist as e:
64 | logger.debug('Tries to access an object that does not exist : ' + str(e))
65 | return None
66 | return obj
67 |
68 |
69 | class RuleSet(CommonMixin, models.Model):
70 | """
71 | Set of Rules. Scripts and signatures.
72 | """
73 | name = models.CharField(max_length=100, unique=True, blank=False, null=False, db_index=True,
74 | verbose_name="Ruleset name")
75 | description = models.CharField(max_length=400, blank=True)
76 | created_date = models.DateTimeField(default=timezone.now, editable=False)
77 | updated_date = models.DateTimeField(blank=True, null=True, editable=False)
78 |
79 | def __str__(self):
80 | return self.name
81 |
82 | @classmethod
83 | def get_by_name(cls, name):
84 | try:
85 | obj = cls.objects.get(name=name)
86 | except cls.DoesNotExist as e:
87 | logger.debug('Tries to access an object that does not exist : ' + str(e))
88 | return None
89 | return obj
90 |
91 |
92 | class Source(CommonMixin, models.Model):
93 | """
94 | Set of Source. For scheduled upload of rules.
95 | """
96 | method = models.ForeignKey(MethodUpload, on_delete=models.CASCADE)
97 | data_type = models.ForeignKey(DataTypeUpload, on_delete=models.CASCADE)
98 | uri = models.CharField(max_length=1000, unique=True, blank=True, null=False)
99 | scheduled_rules_deployment_enabled = models.BooleanField(default=False)
100 | scheduled_rules_deployment_crontab = models.ForeignKey(CrontabSchedule, blank=True, null=True,
101 | on_delete=models.CASCADE)
102 | scheduled_deploy = models.BooleanField(default=False)
103 | file = models.FileField(name='file', upload_to='tmp/source/' + str(time.time()), blank=True)
104 | type = models.CharField(max_length=100, blank=True, default='', editable=False)
105 |
106 | def __str__(self):
107 | return self.uri
108 |
109 | @classmethod
110 | def get_by_uri(cls, uri):
111 | try:
112 | obj = cls.objects.get(uri=uri)
113 | except cls.DoesNotExist as e:
114 | logger.debug('Tries to access an object that does not exist : ' + str(e))
115 | return None
116 | return obj
117 |
--------------------------------------------------------------------------------
/probemanager/rules/templates/rules/search.html:
--------------------------------------------------------------------------------
1 | {% extends "core/base.html" %}
2 |
3 | {% block title %}Result Rules{% endblock %}
4 |
5 | {% block content %}
6 |
7 | Search result for the pattern : {{ pattern }}
8 |
9 | {% if message %}
10 |
11 | {{ message }}
12 |
13 | {% else %}
14 |
15 |
16 |
17 | {% for app in data %}
18 |
{{ app.name }} rules :
19 |
20 | {% for signature in app.signatures %}
21 |
{{ signature }}
23 | {% endfor %}
24 |
25 |
26 | {% for script in app.scripts %}
27 |
{{ script }}
29 | {% endfor %}
30 |
31 |
32 | {% for rule in app.rules %}
33 |
{{ rule }}
35 | {% endfor %}
36 |
37 | {% endfor %}
38 |
39 |
40 |
41 |
42 | {% endif %}
43 |
44 | {% endblock %}
45 |
--------------------------------------------------------------------------------
/probemanager/rules/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/rules/tests/__init__.py
--------------------------------------------------------------------------------
/probemanager/rules/tests/test_models.py:
--------------------------------------------------------------------------------
1 | """ venv/bin/python probemanager/manage.py test rules.tests.test_models --settings=probemanager.settings.dev """
2 | from django.db.utils import IntegrityError
3 | from django.test import TestCase
4 | from django.utils import timezone
5 |
6 | from rules.models import DataTypeUpload, MethodUpload, Source, RuleSet, Rule
7 |
8 |
9 | class DataTypeUploadTest(TestCase):
10 | fixtures = ['init']
11 |
12 | @classmethod
13 | def setUpTestData(cls):
14 | pass
15 |
16 | def test_data_type_upload(self):
17 | all_data_type_upload = DataTypeUpload.get_all()
18 | data_type_upload = DataTypeUpload.get_by_id(1)
19 | self.assertEqual(len(all_data_type_upload), 2)
20 | self.assertEqual(data_type_upload.name, "one file not compressed")
21 | self.assertEqual(str(data_type_upload), "one file not compressed")
22 |
23 | data_type_upload = DataTypeUpload.get_by_name("one file not compressed")
24 | self.assertEqual(data_type_upload.name, "one file not compressed")
25 | with self.assertLogs('rules.models', level='DEBUG'):
26 | DataTypeUpload.get_by_name("https")
27 |
28 | data_type_upload = DataTypeUpload.get_by_id(99)
29 | self.assertEqual(data_type_upload, None)
30 | with self.assertRaises(AttributeError):
31 | data_type_upload.name
32 | with self.assertRaises(IntegrityError):
33 | DataTypeUpload.objects.create(name="one file not compressed")
34 |
35 |
36 | class MethodUploadTest(TestCase):
37 | fixtures = ['init']
38 |
39 | @classmethod
40 | def setUpTestData(cls):
41 | pass
42 |
43 | def test_method_upload(self):
44 | all_method_upload = MethodUpload.get_all()
45 | method_upload = MethodUpload.get_by_id(1)
46 | self.assertEqual(len(all_method_upload), 3)
47 | self.assertEqual(method_upload.name, "Upload file")
48 | self.assertEqual(str(method_upload), "Upload file")
49 |
50 | method_upload = MethodUpload.get_by_name("Upload file")
51 | self.assertEqual(method_upload.name, "Upload file")
52 | with self.assertLogs('rules.models', level='DEBUG'):
53 | MethodUpload.get_by_name("https")
54 |
55 | method_upload = MethodUpload.get_by_id(99)
56 | self.assertEqual(method_upload, None)
57 | with self.assertRaises(AttributeError):
58 | method_upload.name
59 | with self.assertRaises(IntegrityError):
60 | MethodUpload.objects.create(name="Upload file")
61 |
62 |
63 | class RuleTest(TestCase):
64 | fixtures = ['init', 'test-rules-rule']
65 |
66 | @classmethod
67 | def setUpTestData(cls):
68 | pass
69 |
70 | def test_rule(self):
71 | self.assertEqual(Rule.get_by_id(1).rev, 0)
72 | self.assertEqual(Rule.get_by_id(1).reference, "http://www.exemple.com")
73 | self.assertEqual(Rule.get_by_id(1).rule_full, """test""")
74 | self.assertTrue(Rule.get_by_id(1).enabled)
75 |
76 | all_rule = Rule.get_all()
77 | rule = Rule.get_by_id(1)
78 | self.assertEqual(len(all_rule), 1)
79 | self.assertEqual(rule.reference, "http://www.exemple.com")
80 | rule = Rule.get_by_id(99)
81 | self.assertEqual(rule, None)
82 | with self.assertRaises(AttributeError):
83 | rule.reference
84 | rules = Rule.find("tes")
85 | self.assertEqual(len(rules), 1)
86 | self.assertEqual(rules[0].reference, "http://www.exemple.com")
87 |
88 |
89 | class RuleSetTest(TestCase):
90 | fixtures = ['init', 'test-rules-ruleset']
91 |
92 | @classmethod
93 | def setUpTestData(cls):
94 | cls.date_now = timezone.now()
95 |
96 | def test_ruleset(self):
97 | self.assertEqual(RuleSet.get_by_id(1).name, "ruleset1")
98 | all_ruleset = RuleSet.get_all()
99 | ruleset = RuleSet.get_by_id(1)
100 | self.assertEqual(len(all_ruleset), 1)
101 | self.assertEqual(ruleset.name, "ruleset1")
102 | self.assertEqual(str(ruleset), "ruleset1")
103 | self.assertEqual(RuleSet.get_by_name('inexist'), None)
104 | ruleset = RuleSet.get_by_id(99)
105 | self.assertEqual(ruleset, None)
106 | with self.assertRaises(AttributeError):
107 | ruleset.name
108 | with self.assertRaises(IntegrityError):
109 | RuleSet.objects.create(name="ruleset1",
110 | description="",
111 | created_date=self.date_now
112 | )
113 |
114 |
115 | class SourceTest(TestCase):
116 | fixtures = ['init', 'crontab', 'test-rules-source']
117 |
118 | @classmethod
119 | def setUpTestData(cls):
120 | pass
121 |
122 | def test_source(self):
123 | all_source = Source.get_all()
124 | source = Source.get_by_id(1)
125 | self.assertEqual(len(all_source), 2)
126 | self.assertEqual(source.method.name, "URL HTTP")
127 | self.assertEqual(source.data_type.name, "one file not compressed")
128 | self.assertEqual(source.uri, "https://sslbl.abuse.ch/blacklist/sslblacklist.rules")
129 | self.assertEqual(str(source), "https://sslbl.abuse.ch/blacklist/sslblacklist.rules")
130 | source = Source.get_by_uri("https://sslbl.abuse.ch/blacklist/sslblacklist.rules")
131 | self.assertEqual(source.data_type.name, "one file not compressed")
132 |
133 | with self.assertLogs('rules.models', level='DEBUG'):
134 | Source.get_by_uri("https://sslbl.abuse.ch/lacklist.rules")
135 | source = Source.get_by_id(99)
136 | self.assertEqual(source, None)
137 | with self.assertRaises(AttributeError):
138 | source.uri
139 | with self.assertRaises(IntegrityError):
140 | Source.objects.create(method=MethodUpload.get_by_id(1),
141 | uri="https://sslbl.abuse.ch/blacklist/sslblacklist.rules",
142 | data_type=DataTypeUpload.get_by_id(1),
143 | )
144 |
--------------------------------------------------------------------------------
/probemanager/rules/tests/test_views.py:
--------------------------------------------------------------------------------
1 | """ venv/bin/python probemanager/manage.py test rules.tests.test_views --settings=probemanager.settings.dev """
2 | from django.contrib.auth.models import User
3 | from django.shortcuts import reverse
4 | from django.test import Client, TestCase
5 | from django.utils import timezone
6 |
7 |
8 | class ViewsRulesTest(TestCase):
9 | fixtures = ['init', 'init-suricata', 'test-suricata-signature', 'test-suricata-script']
10 |
11 | def setUp(self):
12 | self.client = Client()
13 | User.objects.create_superuser(username='testuser', password='12345', email='testuser@test.com')
14 | if not self.client.login(username='testuser', password='12345'):
15 | self.assertRaises(Exception("Not logged"))
16 | self.date_now = timezone.now()
17 |
18 | def test_search(self):
19 | """
20 | Search page
21 | """
22 | response = self.client.get(reverse('rules:search'), {'pattern': 'DNS'})
23 | self.assertEqual(response.status_code, 200)
24 | self.assertIn('Result Rules', str(response.content))
25 | self.assertEqual('rules/search.html', response.templates[0].name)
26 | self.assertIn('rules', response.resolver_match.app_names)
27 | self.assertIn('function search', str(response.resolver_match.func))
28 | self.assertEqual(str(response.context['user']), 'testuser')
29 | with self.assertTemplateUsed('rules/search.html'):
30 | self.client.get(reverse('rules:search'), {'pattern': 'DNS'})
31 | response = self.client.get(reverse('rules:search'))
32 | self.assertEqual(response.status_code, 200)
33 | self.assertIn('Pattern not found', str(response.content))
34 | response = self.client.get(reverse('rules:search'), {'pattern': ''})
35 | self.assertEqual(response.status_code, 200)
36 | self.assertIn('Pattern not found', str(response.content))
37 | response = self.client.get(reverse('rules:search'), {'pattern': ' '})
38 | self.assertEqual(response.status_code, 200)
39 | self.assertIn('Pattern not found', str(response.content))
40 | response = self.client.get(reverse('rules:search'), {'pattern': '-oywz'})
41 | self.assertEqual(response.status_code, 200)
42 | self.assertIn('Result Rules', str(response.content))
43 |
--------------------------------------------------------------------------------
/probemanager/rules/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from rules import views
4 |
5 | app_name = 'rules'
6 |
7 | urlpatterns = [
8 | url(r'^search$', views.search, name='search'),
9 | ]
10 |
--------------------------------------------------------------------------------
/probemanager/rules/views.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | import logging
3 |
4 | from django.apps.registry import apps
5 | from django.contrib.auth.decorators import login_required
6 | from django.shortcuts import render
7 |
8 | from rules.models import Rule
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 |
13 | @login_required
14 | def search(request):
15 | """
16 | Search rules (Scripts and Signatures) from all the probes.
17 | """
18 | if request.GET.get("pattern"):
19 | pattern = request.GET.get("pattern")
20 | if pattern != "" and pattern != " ":
21 | data = list()
22 | for app in apps.get_app_configs():
23 | first = True
24 | for model in app.get_models():
25 | if issubclass(model, Rule):
26 | if app.verbose_name != "Rules":
27 | if first:
28 | first = False
29 | probe_data = dict()
30 | try:
31 | my_class = getattr(importlib.import_module(app.label + ".models"),
32 | 'Signature' + app.verbose_name)
33 | signatures = my_class.find(pattern)
34 | probe_data.update({'signatures': signatures})
35 |
36 | my_class = getattr(importlib.import_module(app.label + ".models"),
37 | 'Script' + app.verbose_name)
38 | scripts = my_class.find(pattern)
39 | probe_data.update({'scripts': scripts})
40 |
41 | my_class = getattr(importlib.import_module(app.label + ".models"),
42 | 'Rule' + app.verbose_name)
43 | rules = my_class.find(pattern)
44 | probe_data.update({'rules': rules})
45 | except AttributeError:
46 | pass
47 | probe_data.update({'name': app.label})
48 | data.append(probe_data)
49 | return render(request, 'rules/search.html', {'data': data, 'pattern': pattern})
50 | else:
51 | return render(request, 'rules/search.html', {'message': 'Pattern ' + pattern + ' not found'})
52 | else:
53 | return render(request, 'rules/search.html', {'message': 'Pattern not found'})
54 |
--------------------------------------------------------------------------------
/probemanager/runtests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import argparse
3 | import glob
4 | import os
5 | import sys
6 |
7 | import django
8 | from django.conf import settings
9 | from django.test.utils import get_runner
10 |
11 |
12 | def runtests():
13 | """ By default tests are executed by alphabetical order """
14 | parser = argparse.ArgumentParser()
15 | parser.add_argument("-a", "--all", help="run all tests - default action", action="store_true", default=False)
16 | parser.add_argument("--app", help="run the tests for this app", nargs='+', action='append', dest='app', default=[])
17 | args = parser.parse_args()
18 | tests = []
19 | tests_all = []
20 | for test in glob.glob(os.path.dirname(os.path.abspath(__file__)) + '/*/tests/test*.py'):
21 | file = os.path.basename(test)
22 | directory = os.path.basename(os.path.normpath(os.path.join(os.path.dirname(test), os.pardir)))
23 | tests_all.append(directory + '.tests.' + str(file.split('.')[0]))
24 |
25 | tests_app = []
26 | for test in tests_all:
27 | for app in args.app:
28 | for ap in app:
29 | if ap in test:
30 | tests_app.append(test)
31 |
32 | if args.all or len(sys.argv) == 1:
33 | tests = tests_all
34 | else:
35 | tests += tests_app
36 |
37 | tests = sorted(tests)
38 | print("Tests that will be launched : " + str(tests))
39 | django.setup()
40 | testrunner = get_runner(settings)
41 | test_runner = testrunner()
42 | failures = test_runner.run_tests(tests)
43 | sys.exit(bool(failures))
44 |
45 |
46 | if __name__ == "__main__":
47 | runtests()
48 |
--------------------------------------------------------------------------------
/probemanager/scripts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/probemanager/scripts/__init__.py
--------------------------------------------------------------------------------
/probemanager/scripts/apache.py:
--------------------------------------------------------------------------------
1 | import os
2 | from string import Template
3 |
4 | from probemanager.settings.prod import APACHE_PORT, PROJECT_NAME
5 |
6 |
7 | def generate_apache_conf(project_name, dest, port):
8 | template_conf = """
9 | WSGIPythonHome ${dest}/venv
10 | WSGIPythonPath ${dest}/${project_name}
11 |
12 |
13 |
14 | Alias /static/ ${dest}/${project_name}/static/
15 |
16 |
17 | Require all granted
18 |
19 |
20 | Alias /docs/ ${dest}/docs/_build/html/
21 |
22 |
23 | Require all granted
24 |
25 |
26 | WSGIScriptAlias / ${dest}/${project_name}/${project_name}/wsgi.py
27 | SetEnv DJANGO_SETTINGS_MODULE ${dest}/${project_name}/${project_name}/settings/prod.py
28 |
29 | LogFormat "%v %h %l %u %t \\"%r\\" %>s %b" serveur_virtuel_commun
30 | CustomLog /var/log/apache2/${project_name}-access.log serveur_virtuel_commun
31 | ErrorLog /var/log/apache2/${project_name}-error.log
32 |
33 |
34 |
35 | Require all granted
36 |
37 |
38 |
39 |
40 | """
41 | t = Template(template_conf)
42 | apache_conf = t.safe_substitute(project_name=project_name, dest=dest, port=port)
43 | if os.path.isdir('/etc/apache2/sites-enabled/'):
44 | install_dir = '/etc/apache2/sites-enabled/'
45 | else:
46 | install_dir = dest
47 | with open(install_dir + 'probemanager.conf', 'w') as f:
48 | f.write(apache_conf)
49 |
50 |
51 | def run(*args):
52 | dest = args[0].rstrip('/')
53 | port = APACHE_PORT
54 | generate_apache_conf(PROJECT_NAME, dest, port)
55 | exit()
56 |
--------------------------------------------------------------------------------
/probemanager/scripts/db_password.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | from getpass import getpass
3 |
4 | from cryptography.fernet import Fernet
5 |
6 |
7 | def encrypt(plain_text, dest):
8 | with open(dest + 'fernet_key.txt') as f:
9 | fernet_key_bytes = bytes(f.read().strip(), 'utf-8')
10 | fernet_key = Fernet(fernet_key_bytes)
11 | return fernet_key.encrypt(plain_text.encode('utf-8')).decode('utf-8')
12 |
13 |
14 | if __name__ == "__main__":
15 | parser = argparse.ArgumentParser()
16 | parser.add_argument("-d", "--dest", help="", default='/etc/')
17 | args = parser.parse_args()
18 | password = getpass('Type the password for the database, followed by [ENTER]: ')
19 | password_encrypted = encrypt(password, args.dest)
20 |
21 | with open(args.dest + 'password_db.txt', 'w') as f:
22 | f.write(password_encrypted)
23 | exit(password)
24 |
--------------------------------------------------------------------------------
/probemanager/scripts/generate_doc.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from django.apps.registry import apps
4 | from django.conf import settings
5 | from jinja2 import Template
6 |
7 | from core.models import Probe
8 |
9 |
10 | def run(*args):
11 | template = """
12 |
13 | {{ name }}
14 | {% for char in range(name|length) -%}
15 | =
16 | {%- endfor %}
17 |
18 | .. toctree::
19 |
20 | {{ module }}
21 |
22 | """
23 | template_include = """.. include:: ../../probemanager/{{ module }}/README.rst
24 |
25 | """
26 | t = Template(template)
27 | t_include = Template(template_include)
28 | with open(settings.ROOT_DIR + '/docs/modules/index.rst', 'w') as f:
29 | for app in apps.get_app_configs():
30 | for model in app.get_models():
31 | if issubclass(model, Probe):
32 | if app.verbose_name != "Core":
33 | path = settings.BASE_DIR + "/" + app.label + "/README.rst"
34 | if os.path.isfile(path):
35 | template_rendered = t.render(module=app.label, name=app.verbose_name)
36 | template_include_rendered = t_include.render(module=app.label)
37 | f_include = open(settings.ROOT_DIR + '/docs/modules/' + app.label + '.rst', 'w')
38 | f_include.write(template_include_rendered)
39 | f_include.close()
40 | f.write(template_rendered)
41 | break
42 | exit(0)
43 |
--------------------------------------------------------------------------------
/probemanager/scripts/remove_in_file.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import re
3 |
4 |
5 | if __name__ == "__main__":
6 | parser = argparse.ArgumentParser()
7 | parser.add_argument("-p", "--remove", help="pattern to remove", default=[], type=str, action='append')
8 | parser.add_argument("-r", "--replace", help="pattern to replace separated by : "
9 | "example= 'ProbeManager:ProbeManager/probemanager/checkcve'",
10 | default=[], type=str, action='append')
11 | parser.add_argument("-f", "--file", help="file")
12 | args = parser.parse_args()
13 | for pattern in args.remove:
14 | with open(args.file, 'r') as f:
15 | replaced = re.sub(pattern, '', f.read())
16 | with open(args.file, 'w') as f:
17 | f.write(replaced)
18 | for pattern in args.replace:
19 | old, new = pattern.split(':')
20 | with open(args.file, 'r') as f:
21 | replaced = re.sub(old, new, f.read())
22 | with open(args.file, 'w') as f:
23 | f.write(replaced)
24 | exit()
25 |
--------------------------------------------------------------------------------
/probemanager/scripts/secrets.py:
--------------------------------------------------------------------------------
1 | import argparse
2 |
3 | from cryptography.fernet import Fernet
4 | from django.utils.crypto import get_random_string
5 |
6 | if __name__ == "__main__":
7 | parser = argparse.ArgumentParser()
8 | parser.add_argument("-d", "--dest", help="", default='/etc/')
9 | args = parser.parse_args()
10 | fernet_key = Fernet.generate_key()
11 |
12 | with open(args.dest + 'fernet_key.txt', 'w') as f:
13 | f.write(fernet_key.decode('utf8'))
14 |
15 | chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
16 | secret_key = get_random_string(50, chars)
17 |
18 | with open(args.dest + 'secret_key.txt', 'w') as f:
19 | f.write(secret_key)
20 | exit()
21 |
--------------------------------------------------------------------------------
/probemanager/scripts/setup_smtp.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import sys
3 | from getpass import getpass
4 | from string import Template
5 |
6 | from cryptography.fernet import Fernet
7 |
8 | template_smtp = """
9 | [EMAIL]
10 | EMAIL_HOST = ${host}
11 | EMAIL_PORT = ${port}
12 | EMAIL_HOST_USER = ${host_user}
13 | DEFAULT_FROM_EMAIL = ${default_from_email}
14 | EMAIL_USE_TLS = ${use_tls}
15 | """
16 |
17 |
18 | def encrypt(plain_text, dest):
19 | with open(dest + 'fernet_key.txt') as f:
20 | fernet_key_bytes = bytes(f.read().strip(), 'utf-8')
21 | fernet_key = Fernet(fernet_key_bytes)
22 | return fernet_key.encrypt(plain_text.encode('utf-8')).decode('utf-8')
23 |
24 |
25 | if __name__ == "__main__":
26 | parser = argparse.ArgumentParser()
27 | parser.add_argument("-d", "--dest", help="", default='/etc/')
28 | args = parser.parse_args()
29 | print("Server SMTP :")
30 | host = input('host : ')
31 | port = input('port : ')
32 | host_user = input('user : ')
33 | host_password = getpass('password : ')
34 | default_from_email = input('default from email : ')
35 | use_tls = input('The SMTP host use TLS ? : (True/False) ')
36 |
37 | t = Template(template_smtp)
38 | final = t.substitute(host=host,
39 | port=port,
40 | host_user=host_user,
41 | default_from_email=default_from_email,
42 | use_tls=str(use_tls)
43 | )
44 |
45 | with open(args.dest + 'conf.ini', 'a', encoding='utf_8') as f:
46 | f.write(final)
47 | with open(args.dest + 'password_email.txt', 'w', encoding='utf_8') as f:
48 | f.write(encrypt(host_password, args.dest))
49 | sys.exit(0)
50 |
--------------------------------------------------------------------------------
/probemanager/scripts/setup_tests.py:
--------------------------------------------------------------------------------
1 | import os
2 | from getpass import getpass
3 | from shutil import copyfile
4 | from string import Template
5 |
6 | from django.conf import settings
7 |
8 | from core.utils import encrypt
9 |
10 | template_server_test = """
11 | [
12 | {
13 | "model": "core.configuration",
14 | "pk": 1,
15 | "fields": {
16 | "key": "PUSHBULLET_API_KEY",
17 | "value": "${pushbullet_key}"
18 | }
19 | },
20 | {
21 | "model": "core.sshkey",
22 | "pk": 1,
23 | "fields": {
24 | "name": "test",
25 | "file": "ssh_keys/${ssh_private_key_file}"
26 | }
27 | },
28 | {
29 | "model": "core.server",
30 | "pk": 1,
31 | "fields": {
32 | "host": "${host}",
33 | "os": 1,
34 | "remote_user": "${remote_user}",
35 | "remote_port": ${remote_port},
36 | "become": ${become }},
37 | "become_method": "${become_method}",
38 | "become_user": "${become_user}",
39 | "become_pass": "${become_pass}",
40 | "ssh_private_key_file": 1
41 | }
42 | }
43 | ]
44 | """
45 |
46 |
47 | def run():
48 | skip = input('Add datas Tests ? (y/N) ')
49 | if skip.lower() == 'n' or not skip:
50 | exit(0)
51 | else:
52 | print("Conf for tests")
53 | pushbullet_key = input("PushBullet API key : ")
54 | print("Server for tests")
55 | host = input('host : ')
56 | become = input('become : (true/false) ')
57 | become_method = input('become_method : ')
58 | become_user = input('become_user : ')
59 | become_pass = getpass('become_pass : ')
60 | remote_user = input('remote_user : ')
61 | remote_port = input('remote_port : (0-65535) ')
62 | ssh_private_key_file = input('ssh_private_key_file : (Absolute file path) ')
63 | ssh_private_key_file_basename = os.path.basename(ssh_private_key_file)
64 | ssh_dir = settings.BASE_DIR + '/ssh_keys/'
65 | if not os.path.exists(ssh_dir):
66 | os.makedirs(ssh_dir)
67 | try:
68 | copyfile(ssh_private_key_file, ssh_dir + ssh_private_key_file_basename)
69 | os.chmod(ssh_dir + ssh_private_key_file_basename, 0o600)
70 | except Exception as e:
71 | print("Error in the path of the file : " + str(e))
72 | exit(1)
73 |
74 | t = Template(template_server_test)
75 | server_test = t.substitute(host=host,
76 | become=become,
77 | become_user=become_user,
78 | become_method=become_method,
79 | become_pass=encrypt(become_pass),
80 | remote_user=remote_user,
81 | remote_port=remote_port,
82 | ssh_private_key_file=ssh_private_key_file_basename,
83 | pushbullet_key=pushbullet_key
84 | )
85 | with open(settings.BASE_DIR + '/core/fixtures/test-core-secrets.json', 'w') as f:
86 | f.write(server_test)
87 | exit(0)
88 |
--------------------------------------------------------------------------------
/probemanager/scripts/utilities.py:
--------------------------------------------------------------------------------
1 | # sudo /usr/local/share/ProbeManager/venv/bin/python /usr/local/share/ProbeManager/probemanager/scripts/utilities.py
2 | # -d /usr/local/share/ProbeManager/
3 | # venv/bin/python probemanager/scripts/utilities.py -d ~/git/Probemanager/
4 | import argparse
5 | import os
6 | import sys
7 | from getpass import getpass
8 |
9 | from cryptography.fernet import Fernet
10 |
11 |
12 | def encrypt(plain_text, dest):
13 | if os.path.exists(dest + 'fernet_key.txt'):
14 | with open(dest + 'fernet_key.txt') as f:
15 | fernet_key_bytes = f.read().strip().encode('utf-8')
16 | else:
17 | from django.conf import settings
18 | fernet_key_bytes = settings.FERNET_KEY
19 | fernet_key = Fernet(fernet_key_bytes)
20 | return fernet_key.encrypt(plain_text.encode('utf-8')).decode('utf-8')
21 |
22 |
23 | if __name__ == "__main__":
24 | parser = argparse.ArgumentParser()
25 | parser.add_argument("-d", "--dest", help="destination", default='/usr/local/share/ProbeManager/')
26 | args = parser.parse_args()
27 | password = getpass('Type the password, followed by [ENTER]: ')
28 | password_encrypted = encrypt(password, args.dest)
29 | print("Password encrypted : " + password_encrypted)
30 | sys.exit(0)
31 |
--------------------------------------------------------------------------------
/probemanager/scripts/version.py:
--------------------------------------------------------------------------------
1 | from django.apps.registry import apps
2 | from django.conf import settings
3 |
4 | from core.git import git_tag
5 | from core.models import Probe
6 |
7 |
8 | def run(*args):
9 | if args[0] and args[1]:
10 | source = args[0] + '/probemanager'
11 | dest = args[1].rstrip('/') + '/probemanager'
12 | else:
13 | source = settings.BASE_DIR
14 | dest = settings.BASE_DIR
15 | # en prod git_tag prendre des sources ou copier git_root = settings.BASE_DIR
16 | with open(dest + '/version.txt', 'w') as f:
17 | f.write(git_tag(source))
18 | for app in apps.get_app_configs():
19 | for model in app.get_models():
20 | if issubclass(model, Probe):
21 | if app.verbose_name != "Core":
22 | dest_app = dest + "/" + app.label + '/'
23 | source_app = source + "/" + app.label + '/'
24 | with open(dest_app + 'version.txt', 'w') as f:
25 | f.write(git_tag(source_app))
26 | exit(0)
27 |
--------------------------------------------------------------------------------
/probemanager/templates/admin/base.html:
--------------------------------------------------------------------------------
1 | {% load i18n static %}
2 | {% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
3 |
4 |
5 | {% block title %}{% endblock %}
6 |
7 | {% block extrastyle %}{% endblock %}
8 | {% if LANGUAGE_BIDI %}{% endif %}
9 | {% block extrahead %}{% endblock %}
10 | {% block blockbots %}{% endblock %}
11 |
12 |
13 |
14 | {% load i18n %}
15 |
16 |
18 |
19 |
20 |
21 |
22 | {% if not is_popup %}
23 |
24 |
52 |
53 | {% block breadcrumbs %}
54 |
58 | {% endblock %}
59 | {% endif %}
60 |
61 | {% block messages %}
62 | {% if messages %}
63 |
{% for message in messages %}
64 | - {{ message|capfirst }}
65 | {% endfor %}
66 | {% endif %}
67 | {% endblock messages %}
68 |
69 |
70 |
71 | {% block pretitle %}{% endblock %}
72 | {% block content_title %}{% if title %}
{{ title }}
{% endif %}{% endblock %}
73 | {% block content %}
74 | {% block object-tools %}{% endblock %}
75 | {{ content }}
76 | {% endblock %}
77 | {% block sidebar %}{% endblock %}
78 |
79 |
80 |
81 |
82 | {% block footer %}{% endblock %}
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/probemanager/templates/admin/base_site.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/base.html" %}
2 |
3 | {% block title %}{{ title }} | Probe Manager site admin{% endblock %}
4 |
5 | {% block branding %}
6 |
7 | {% endblock %}
8 |
9 | {% block nav-global %}{% endblock %}
10 |
--------------------------------------------------------------------------------
/probemanager/templates/admin/index.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/base_site.html" %}
2 | {% load i18n static %}
3 |
4 | {% block extrastyle %}{{ block.super }}{% endblock %}
5 |
6 | {% block coltype %}colMS{% endblock %}
7 |
8 | {% block bodyclass %}{{ block.super }} dashboard{% endblock %}
9 |
10 | {% block breadcrumbs %}{% endblock %}
11 |
12 | {% block content %}
13 |
14 | {% load version %}
Probe Manager {% version 'ProbeManager' %}
15 |
16 | {% if app_list %}
17 | {% for app in app_list %}
18 |
51 | {% endfor %}
52 | {% else %}
53 |
{% trans "You don't have permission to edit anything." %}
54 | {% endif %}
55 |
56 | {% endblock %}
57 |
58 | {% block sidebar %}
59 |
60 |
61 |
{% trans 'Recent actions' %}
62 |
{% trans 'My actions' %}
63 | {% load log %}
64 | {% get_admin_log 10 as admin_log for_user user %}
65 | {% if not admin_log %}
66 |
{% trans 'None available' %}
67 | {% else %}
68 |
69 | {% for entry in admin_log %}
70 | -
71 | {% if entry.is_deletion or not entry.get_admin_url %}
72 | {{ entry.object_repr }}
73 | {% else %}
74 | {{ entry.object_repr }}
75 | {% endif %}
76 |
77 | {% if entry.content_type %}
78 | {% filter capfirst %}{{ entry.content_type }}{% endfilter %}
79 | {% else %}
80 | {% trans 'Unknown content' %}
81 | {% endif %}
82 |
83 | {% endfor %}
84 |
85 | {% endif %}
86 |
87 |
88 | {% endblock %}
89 |
--------------------------------------------------------------------------------
/requirements/base.txt:
--------------------------------------------------------------------------------
1 | amqp==2.3.2
2 | bcrypt==3.1.4
3 | celery==4.2.0
4 | Django==2.0.11
5 | django-celery-beat==1.1.1
6 | django-celery-results==1.0.1
7 | django-extensions==2.0.7
8 | django-rest-swagger==2.2.0
9 | django-select2-forms==2.0.2
10 | djangorestframework==3.8.2
11 | Jinja2==2.10
12 | lxml==4.2.3
13 | paramiko==2.4.2
14 | psutil==5.4.6
15 | psycopg2-binary==2.7.5
16 | pushbullet.py==0.11.0
17 | pymisp==2.4.92.1
18 | PyYAML==4.2b1
19 | Sphinx==1.7.5
20 | sphinx-rtd-theme==0.4.0
21 |
--------------------------------------------------------------------------------
/requirements/dev.txt:
--------------------------------------------------------------------------------
1 | -r base.txt
2 |
--------------------------------------------------------------------------------
/requirements/os/debian.txt:
--------------------------------------------------------------------------------
1 | build-essential
2 | libpq-dev
3 | postgresql
4 | python3-venv
5 | python3-dev
6 | python3-wheel
7 | rabbitmq-server
8 | wget
9 |
--------------------------------------------------------------------------------
/requirements/os/debian_prod.txt:
--------------------------------------------------------------------------------
1 | apache2
2 | libapache2-mod-wsgi-py3
3 |
--------------------------------------------------------------------------------
/requirements/os/osx.txt:
--------------------------------------------------------------------------------
1 | postgresql
2 | python3
3 | rabbitmq
4 |
--------------------------------------------------------------------------------
/requirements/os/osx_prod.txt:
--------------------------------------------------------------------------------
1 | apache2
2 | mod_wsgi
3 |
--------------------------------------------------------------------------------
/requirements/prod.txt:
--------------------------------------------------------------------------------
1 | -r base.txt
2 |
--------------------------------------------------------------------------------
/requirements/test.txt:
--------------------------------------------------------------------------------
1 | coverage==4.5.1
2 | django-coverage-plugin==1.5.0
3 | flake8==3.5.0
4 | codacy-coverage==1.3.11
5 |
--------------------------------------------------------------------------------
/secrets.tar.enc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treussart/ProbeManager/afef59036ca7e279a9125b226721fe3d237f04be/secrets.tar.enc
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ -z $1 ] || [[ "$1" = 'dev' ]]; then
4 | arg="dev"
5 | elif [[ "$1" = 'prod' ]]; then
6 | arg=$1
7 | else
8 | echo 'Bad argument'
9 | exit 1
10 | fi
11 |
12 | export DJANGO_SETTINGS_MODULE="probemanager.settings.$arg"
13 | export PYTHONPATH=$PYTHONPATH:"$destfull"/probemanager
14 |
15 | # Virtualenv
16 | if [[ "$VIRTUAL_ENV" = "" ]]; then
17 | if [ ! -d venv ]; then
18 | echo 'install before starting the server'
19 | exit
20 | else
21 | source venv/bin/activate
22 | fi
23 | fi
24 | # Celery
25 | if [ ! -f probemanager/celery.pid ]; then
26 | (cd probemanager/ && celery -A probemanager worker -D --pidfile celery.pid -B -l debug -f probemanager-celery.log --scheduler django_celery_beat.schedulers:DatabaseScheduler)
27 | else
28 | kill $( cat probemanager/celery.pid)
29 | sleep 3
30 | pkill -f celery
31 | sleep 3
32 | (cd probemanager/ && celery -A probemanager worker -D --pidfile celery.pid -B -l debug -f probemanager-celery.log --scheduler django_celery_beat.schedulers:DatabaseScheduler)
33 | fi
34 | # Server
35 | python probemanager/manage.py runserver --settings=probemanager.settings.$arg
36 |
--------------------------------------------------------------------------------
/stop_celery.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | pkill -f celery
4 |
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Get args
4 | if [ -z $1 ]; then
5 | arg=""
6 | else
7 | arg='--app '$@
8 | fi
9 | source=''
10 | sourcecoverage=''
11 | for app in $@
12 | do
13 | source="$source"" probemanager/""$app"
14 | sourcecoverage="$sourcecoverage""probemanager/""$app"","
15 | done
16 | sourcecoverage="--source=""$sourcecoverage"
17 |
18 | if [[ "$VIRTUAL_ENV" = "" ]]; then
19 | if [ ! -d venv ]; then
20 | echo 'Install before testing'
21 | exit
22 | else
23 | source venv/bin/activate
24 | fi
25 | fi
26 |
27 | if [[ "$TRAVIS" = true ]]; then
28 | export DJANGO_SETTINGS_MODULE="probemanager.settings.dev"
29 | LOG_PATH="/var/log/"
30 | TRAVIS_JOB_NUM_MIN=$( echo $TRAVIS_JOB_NUMBER | cut -f2 -d '.' )
31 | else
32 | export DJANGO_SETTINGS_MODULE="probemanager.settings.dev"
33 | LOG_PATH="probemanager/"
34 | fi
35 |
36 | # test if fixtures secrets files are here
37 | if [ ! -f probemanager/core/fixtures/test-core-secrets.json ]; then
38 | echo 'Secrets fixtures not found'
39 | exit 1
40 | fi
41 | FAIL_UNDER="90"
42 | flake8 $source --config=.flake8
43 | result_flake8="$?"
44 | if [ "$result_flake8" -ne 0 ]; then
45 | echo "Tests failed : PEP-8 Not Compliant"
46 | exit "$result_flake8"
47 | fi
48 | coverage erase
49 | coverage run $sourcecoverage probemanager/runtests.py $arg
50 | result_run="$?"
51 | if [ "$result_run" -ne 0 ]; then
52 | echo "Tests failed"
53 | exit "$result_run"
54 | fi
55 | coverage report --fail-under="$FAIL_UNDER"
56 | result_report="$?"
57 | coverage html --skip-covered
58 | if [ "$result_report" -ne 0 ]; then
59 | echo "Tests failed : Coverage under $FAIL_UNDER %"
60 | exit "$result_report"
61 | fi
62 |
63 | if [[ "$CODACY_PROJECT_TOKEN" != "" && "$TRAVIS_JOB_NUM_MIN" = "1" ]]; then
64 | coverage xml
65 | python-codacy-coverage -r coverage.xml
66 |
67 | coverage xml -o coverage-suricata.xml --include='probemanager/suricata/*'
68 | python probemanager/scripts/remove_in_file.py -p probemanager/suricata/ -p probemanager.suricata -r ProbeManager:ProbeManager/probemanager/suricata -f coverage-suricata.xml
69 | ( cd probemanager/suricata && python-codacy-coverage -r ../../coverage-suricata.xml -t $CODACY_SURICATA_TOKEN )
70 |
71 | coverage xml -o coverage-checkcve.xml --include='probemanager/checkcve/*'
72 | python probemanager/scripts/remove_in_file.py -p probemanager/checkcve/ -p probemanager.checkcve -r ProbeManager:ProbeManager/probemanager/checkcve -f coverage-checkcve.xml
73 | ( cd probemanager/checkcve && python-codacy-coverage -r ../../coverage-checkcve.xml -t $CODACY_CHECKCVE_TOKEN )
74 |
75 | coverage xml -o coverage-bro.xml --include='probemanager/bro/*'
76 | python probemanager/scripts/remove_in_file.py -p probemanager/bro/ -p probemanager.bro -r ProbeManager:ProbeManager/probemanager/bro -f coverage-bro.xml
77 | ( cd probemanager/bro && python-codacy-coverage -r ../../coverage-bro.xml -t $CODACY_BRO_TOKEN )
78 | fi
79 | if [[ -f "$LOG_PATH"probemanager-error.log && "$TRAVIS" = true ]]; then
80 | echo "#### ERROR LOGS ####"
81 | cat "$LOG_PATH"probemanager-error.log
82 | fi
83 | if [[ -f "$LOG_PATH"probemanager-celery.log && "$TRAVIS" = true ]]; then
84 | echo "#### CELERY LOGS ####"
85 | cat "$LOG_PATH"probemanager-celery.log
86 | fi
87 |
88 | exit
89 |
--------------------------------------------------------------------------------