.. _custom_checks: Custom Service Checks ===================== .. versionadded:: 1.6 .. attention:: Custom Services are an advanced feature of IScorE. The vast majority of the time, you will not need them. If you need the service scanner to perform a check that is not one of the :ref:`built-in checks `, IScorE has support for custom service checks. IScorE searches all the apps listed in ``INSTALLED_APPS`` for a ``services.py`` file, which it will then load. Service check types must be defined in a ``services.py`` to be recognized by IScorE. Service checks are defined by creating a sub-class of :py:class:`~iscore.servicecheck.registry.ServiceType` and registering it with the :py:class:`~iscore.servicecheck.registry.ServiceRegistry`. In general, IScorE has three kinds of service checks: Standard Checks, Multi-Protocol Checks, and Mass Checks. Standard checks are your typical http/ssh/database checks. They check one server for one service and report the results. Multi-Protocol checks are somewhat of a "meta" check. They determine which service type should be used on a per-team basis based on a criteria defined in the service type. Mass checks check all teams at once, which is useful when integrating with an external scoring engine for specialty checks (i.e. SCADA). .. _service_lifecycle: Service Check Lifecycle ----------------------- Registration ^^^^^^^^^^^^ .. method:: iscore.servicecheck.registry.ServiceRegistry.register(cls) :noindex: .. function:: iscore.servicecheck.register(cls) :noindex: Service Types **must** be registered with the :py:class:`~iscore.servicecheck.registry.ServiceRegistry` to be recognized by IScorE. Registration is done in ``services.py`` within a Django app that is listed in ``INSTALLED_APPS``. The simplest way to register your type is to use the :py:func:`~iscore.servicecheck.register` decorator. Check Start ^^^^^^^^^^^ .. method:: iscore.servicecheck.registry.ServiceType.start_check(service) :noindex: When a service scan runs, it spawns a thread per team (:py:class:`~iscore.servicecheck.registry.TeamWorker`) which then goes on the spawn a thread per service check for that team (:py:class:`~iscore.servicecheck.registry.ServiceWorker`). Before the team worker spawns the service worker for a check, it calls :py:meth:`~iscore.servicecheck.registry.ServiceType.start_check`. This method is the last method called with the check that is allowed to directly access the database. Database access in a service worker is not allowed. In the base implementation, this method marks the check as in progress. If you override this method, you **must** call ``super(NameOfClass, self).start_check(check)``. Pre-Check ^^^^^^^^^ .. method:: iscore.servicecheck.registry.ServiceType.resolve_multi(service) :noindex: .. method:: iscore.servicecheck.registry.ServiceType.pre_check(service) :noindex: During the pre-check phase, the service worker first attempts to resolve any multi-protocol service types. To do so, it calls :py:meth:`~iscore.servicecheck.registry.ServiceType.resolve_multi` which it expects to return the short name of the check that should actually use, or the string ``"self"``, which is a special name meaning to use itself. In the base implementation, this method returns ``"self"``. .. warning:: Returning the name of another multi-protocol check from ``resolve_multi`` is not supported. IScorE will simply attempt to use the multi-protocol check as if it were a standard check. Once multi-protocol checks have been resolved, the worker calls :py:meth:`~iscore.servicecheck.registry.ServiceType.pre_check`. Anything that needs to be done before the check (e.g. setting up storing state) should be done in this method. .. note:: If the check is a mass check, ``resolve_multi`` gets the first service object and ``pre_check`` gets a list of all service objects. Check ^^^^^ .. method:: iscore.servicecheck.registry.ServiceType.do_check(service) :noindex: .. method:: iscore.servicecheck.registry.ServiceType.do_mass_check(service) :noindex: ``do_check`` and ``do_mass_check`` perform the actually Standard and Mass checks respectively. Override whichever is appropriate for the type of check you are creating. Overriding the appropriate check method is **required**, the base implementation simply raises a :py:class:`NotImplemented` exception. To report the results of the scan back to the scoring engine, simply call :py:meth:`~iscore.servicecheck.registry.ServiceType.report_score` with the service, score, and an error message if relevant. The error message should either be the error message returned when attempting the check, or a custom error message if it would be more helpful for the blue teams. Remember that this message is what is displayed on the service scanner page for failed scans. Some examples:: # Score of 5 with no errors self.report_score(service, 5, None) # Failed check with error self.report_score(service, 0, "Connection timed-out") The check is also expected to emit a log message detailing the status of the check. A helper method exists in order to standardize the messages to make them more parsable. Simply call :py:meth:`~iscore.servicecheck.registry.ServiceType.log` to log a message in the standard format, to the scan logger. Some examples can be found below:: # : FAIL TeamN : SSH login to self.log(service, "login", False) # : Success TeamN : HTTP connect to self.log(service, "connect", True) .. method:: iscore.servicecheck.registry.ServiceType.get_credentials(service) :noindex: If the check uses credentials (Team Specific info, Team Info Pools, explicit username and password) this method will return the username and password. By default, this method simply returns ``service.username`` and ``service.password``. However, depending on the check it may be useful for this method to mutate the username or password to match what the service expects. For example, the LDAP service check mutates the username to follow the ``username@domain`` pattern. .. note:: The Team Worker automatically sets ``service.username`` and ``service.password`` to the correct value if the service is configured to use Team Specific Info or Team Info Pools. Post-Check ^^^^^^^^^^ .. method:: iscore.servicecheck.registry.ServiceType.post_check(service) :noindex: Immediately after the Check phase, the service worker calls ``post_check``. Any cleanup that needs to be done after a check should be done in this method. End Check ^^^^^^^^^ .. method:: iscore.servicecheck.registry.ServiceType.end_check(service) :noindex: Run by the Team Worker to mark a check as finished. You should not need to override this, but if you do you **must** call ``super(NameOfClass, self).end_check(service)``. Reference --------- .. toctree:: registry_reference