diff --git a/.zuul.yaml b/.zuul.yaml index 7ccbd0a..ba88a7c 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -3,6 +3,87 @@ check: jobs: - openstack-tox-linters + - k8sapp-auditd-tox-py27 + - k8sapp-auditd-tox-py36 + - k8sapp-auditd-tox-flake8 + - k8sapp-auditd-tox-pylint + - k8sapp-auditd-tox-bandit gate: jobs: - openstack-tox-linters + - k8sapp-auditd-tox-py27 + - k8sapp-auditd-tox-py36 + - k8sapp-auditd-tox-flake8 + - k8sapp-auditd-tox-pylint + - k8sapp-auditd-tox-bandit + +- job: + name: k8sapp-auditd-tox-py27 + parent: tox + description: | + Run py27 test for k8sapp_auditd + nodeset: ubuntu-bionic + required-projects: + - starlingx/config + - starlingx/fault + - starlingx/update + - starlingx/utilities + files: + - python-k8sapp-auditd/* + vars: + tox_envlist: py27 + tox_extra_args: -c python-k8sapp-auditd/k8sapp_auditd/tox.ini +- job: + name: k8sapp-auditd-tox-py36 + parent: tox + description: | + Run py36 test for k8sapp_auditd + nodeset: ubuntu-bionic + required-projects: + - starlingx/config + - starlingx/fault + - starlingx/update + - starlingx/utilities + files: + - python-k8sapp-auditd/* + vars: + tox_envlist: py36 + tox_extra_args: -c python-k8sapp-auditd/k8sapp_auditd/tox.ini +- job: + name: k8sapp-auditd-tox-flake8 + parent: tox + description: | + Run flake8 test for k8sapp_auditd + nodeset: ubuntu-bionic + files: + - python-k8sapp-auditd/* + vars: + tox_envlist: flake8 + tox_extra_args: -c python-k8sapp-auditd/k8sapp_auditd/tox.ini +- job: + name: k8sapp-auditd-tox-pylint + parent: tox + description: | + Run pylint test for k8sapp_auditd + nodeset: ubuntu-bionic + required-projects: + - starlingx/config + - starlingx/fault + - starlingx/update + - starlingx/utilities + files: + - python-k8sapp-auditd/* + vars: + tox_envlist: pylint + tox_extra_args: -c python-k8sapp-auditd/k8sapp_auditd/tox.ini +- job: + name: k8sapp-auditd-tox-bandit + parent: tox + description: | + Run bandit test for k8sapp_auditd + nodeset: ubuntu-bionic + files: + - python-k8sapp-auditd/* + vars: + tox_envlist: bandit + tox_extra_args: -c python-k8sapp-auditd/k8sapp_auditd/tox.ini diff --git a/centos_pkg_dirs b/centos_pkg_dirs index cc62b53..45cb1d2 100644 --- a/centos_pkg_dirs +++ b/centos_pkg_dirs @@ -1 +1,2 @@ stx-audit-helm +python-k8sapp-auditd \ No newline at end of file diff --git a/centos_pkg_dirs_containers b/centos_pkg_dirs_containers index cc62b53..45cb1d2 100644 --- a/centos_pkg_dirs_containers +++ b/centos_pkg_dirs_containers @@ -1 +1,2 @@ stx-audit-helm +python-k8sapp-auditd \ No newline at end of file diff --git a/python-k8sapp-auditd/centos/build_srpm.data b/python-k8sapp-auditd/centos/build_srpm.data new file mode 100644 index 0000000..bcf4e1c --- /dev/null +++ b/python-k8sapp-auditd/centos/build_srpm.data @@ -0,0 +1,9 @@ +SRC_DIR="k8sapp_auditd" +OPT_DEP_LIST="$STX_BASE/audit-armada-app/stx-audit-helm" + +TIS_PATCH_VER=GITREVCOUNT + +# Keep the SRCREV in sync with stx-auditd-helm so the app version is the same +# as the plugin version + +TIS_BASE_SRCREV=eeb94bddc8c4c6e513adfae5505e174e0445deed \ No newline at end of file diff --git a/python-k8sapp-auditd/centos/python-k8sapp-audit.spec b/python-k8sapp-auditd/centos/python-k8sapp-audit.spec new file mode 100644 index 0000000..eb9bf5a --- /dev/null +++ b/python-k8sapp-auditd/centos/python-k8sapp-audit.spec @@ -0,0 +1,52 @@ +%global app_name auditd +%global pypi_name k8sapp-auditd +%global sname k8sapp_auditd + +Name: python-%{pypi_name} +Version: 1.0 +Release: %{tis_patch_ver}%{?_tis_dist} +Summary: StarlingX sysinv extensions: Auditd + +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch + +BuildRequires: python-setuptools +BuildRequires: python-pbr +BuildRequires: python2-pip +BuildRequires: python2-wheel + +%description +StarlingX sysinv extensions: AUDITD K8S app + +%prep +%setup +# Remove bundled egg-info +rm -rf %{pypi_name}.egg-info + +%build +export PBR_VERSION=%{version} +%{__python2} setup.py build + +%py2_build_wheel + +%install +export PBR_VERSION=%{version}.%{tis_patch_ver} +export SKIP_PIP_INSTALL=1 +%{__python2} setup.py install --skip-build --root %{buildroot} +mkdir -p ${RPM_BUILD_ROOT}/plugins/%{app_name} +install -m 644 dist/*.whl ${RPM_BUILD_ROOT}/plugins/%{app_name}/ + +%files +%{python2_sitelib}/%{sname} +%{python2_sitelib}/%{sname}-*.egg-info + +%package wheels +Summary: %{name} wheels + +%description wheels +Contains python wheels for %{name} + +%files wheels +/plugins/* diff --git a/python-k8sapp-auditd/k8sapp_auditd/.gitignore b/python-k8sapp-auditd/k8sapp_auditd/.gitignore new file mode 100644 index 0000000..78c457c --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/.gitignore @@ -0,0 +1,35 @@ +# Compiled files +*.py[co] +*.a +*.o +*.so + +# Sphinx +_build +doc/source/api/ + +# Packages/installer info +*.egg +*.egg-info +dist +build +eggs +parts +var +sdist +develop-eggs +.installed.cfg + +# Other +*.DS_Store +.stestr +.testrepository +.tox +.venv +.*.swp +.coverage +bandit.xml +cover +AUTHORS +ChangeLog +*.sqlite diff --git a/python-k8sapp-auditd/k8sapp_auditd/.stestr.conf b/python-k8sapp-auditd/k8sapp_auditd/.stestr.conf new file mode 100644 index 0000000..dea15ea --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/.stestr.conf @@ -0,0 +1,4 @@ +[DEFAULT] +test_path=./k8sapp_auditd/tests +top_dir=./k8sapp_auditd +#parallel_class=True diff --git a/python-k8sapp-auditd/k8sapp_auditd/LICENSE b/python-k8sapp-auditd/k8sapp_auditd/LICENSE new file mode 100644 index 0000000..d6e2801 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Wind River Systems, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/python-k8sapp-auditd/k8sapp_auditd/README.rst b/python-k8sapp-auditd/k8sapp_auditd/README.rst new file mode 100644 index 0000000..0bb23af --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/README.rst @@ -0,0 +1,7 @@ +k8sapp-audit +=================== + +This project contains StarlingX Kubernetes application specific python plugins +for auditd. These plugins are required to integrate the auditd application into +the StarlingX application framework and to support the various StarlingX +deployments. diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/__init__.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/armada/__init__.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/armada/__init__.py new file mode 100644 index 0000000..9bc4dd1 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/armada/__init__.py @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import yaml + + +class quoted_str(str): + pass + + +# force strings to be single-quoted to avoid interpretation as numeric values +def quoted_presenter(dumper, data): + return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style="'") + + +yaml.add_representer(quoted_str, quoted_presenter) diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/armada/manifest_auditd.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/armada/manifest_auditd.py new file mode 100644 index 0000000..19faf1c --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/armada/manifest_auditd.py @@ -0,0 +1,32 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# All Rights Reserved. +# + +""" System inventory Armada manifest operator.""" + +from k8sapp_auditd.common import constants as app_constants +from k8sapp_auditd.helm.auditd import AuditdHelm + +from sysinv.helm import manifest_base as base + + +class AuditdArmadaManifestOperator(base.ArmadaManifestOperator): + + APP = app_constants.HELM_APP_AUDITD + ARMADA_MANIFEST = 'armada-manifest' + + CHART_GROUP_AUDITD = 'auditd' + CHART_GROUPS_LUT = { + AuditdHelm.CHART: 'kube-system-auditd', + } + + def platform_mode_manifest_updates(self, dbapi, mode): + """ Update the application manifest based on the platform + + :param dbapi: DB api object + :param mode: mode to control how to apply the application manifest + """ diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/common/__init__.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/common/constants.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/common/constants.py new file mode 100644 index 0000000..3a8a27e --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/common/constants.py @@ -0,0 +1,15 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Application Name +HELM_APP_AUDITD = 'auditd' + +# Namespace to deploy the application +HELM_NS_AUDITD = 'kube-system' + +# Helm: Supported charts: +# These values match the names in the chart package's Chart.yaml +HELM_CHART_AUDITD = 'auditd' diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/helm/__init__.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/helm/__init__.py new file mode 100644 index 0000000..9bc4dd1 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/helm/__init__.py @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import yaml + + +class quoted_str(str): + pass + + +# force strings to be single-quoted to avoid interpretation as numeric values +def quoted_presenter(dumper, data): + return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style="'") + + +yaml.add_representer(quoted_str, quoted_presenter) diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/helm/auditd.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/helm/auditd.py new file mode 100644 index 0000000..a46f329 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/helm/auditd.py @@ -0,0 +1,41 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from sysinv.common import exception +from sysinv.helm import base + +from k8sapp_auditd.common import constants as app_constants + + +class AuditdHelm(base.BaseHelm): + """Class to encapsulate helm operations for the auditd chart""" + + SUPPORTED_NAMESPACES = base.BaseHelm.SUPPORTED_NAMESPACES + \ + [app_constants.HELM_NS_AUDITD] + SUPPORTED_APP_NAMESPACES = { + app_constants.HELM_APP_AUDITD: + base.BaseHelm.SUPPORTED_NAMESPACES + [app_constants.HELM_NS_AUDITD], + } + + CHART = app_constants.HELM_CHART_AUDITD + + SERVICE_NAME = app_constants.HELM_CHART_AUDITD + + def get_namespaces(self): + return self.SUPPORTED_NAMESPACES + + def get_overrides(self, namespace=None): + overrides = { + app_constants.HELM_NS_AUDITD: {} + } + + if namespace in self.SUPPORTED_NAMESPACES: + return overrides[namespace] + elif namespace: + raise exception.InvalidHelmNamespace(chart=self.CHART, + namespace=namespace) + else: + return overrides diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/__init__.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/test_auditd.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/test_auditd.py new file mode 100644 index 0000000..0c4b019 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/test_auditd.py @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from k8sapp_auditd.tests import test_plugins + +from sysinv.db import api as dbapi +from sysinv.tests.db import utils as dbutils +from sysinv.tests.helm import base + + +class AuditdTestCase(test_plugins.K8SAppAuditdAppMixin, + base.HelmTestCaseMixin): + + def setUp(self): + super(AuditdTestCase, self).setUp() + self.app = dbutils.create_test_app(name='auditd') + self.dbapi = dbapi.get_instance() diff --git a/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/test_plugins.py b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/test_plugins.py new file mode 100644 index 0000000..6374128 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/k8sapp_auditd/tests/test_plugins.py @@ -0,0 +1,43 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from k8sapp_auditd.common import constants as app_constants +from sysinv.tests.helm.test_helm import HelmOperatorTestSuiteMixin + +from sysinv.tests.db import base as dbbase + + +class K8SAppAuditdAppMixin(object): + app_name = app_constants.HELM_APP_AUDITD + path_name = app_name + '.tgz' + + def setUp(self): + super(K8SAppAuditdAppMixin, self).setUp() + + +# Test Configuration: +# - Controller +# - IPv6 +# - Ceph Storage +# - auditd app +class K8SAppAuditdControllerTestCase(K8SAppAuditdAppMixin, + dbbase.BaseIPv6Mixin, + dbbase.BaseCephStorageBackendMixin, + HelmOperatorTestSuiteMixin, + dbbase.ControllerHostTestCase): + pass + + +# Test Configuration: +# - AIO +# - IPv4 +# - Ceph Storage +# - auditd app +class K8SAppAuditdAIOTestCase(K8SAppAuditdAppMixin, + dbbase.BaseCephStorageBackendMixin, + HelmOperatorTestSuiteMixin, + dbbase.AIOSimplexHostTestCase): + pass diff --git a/python-k8sapp-auditd/k8sapp_auditd/pylint.rc b/python-k8sapp-auditd/k8sapp_auditd/pylint.rc new file mode 100644 index 0000000..6e869c3 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/pylint.rc @@ -0,0 +1,238 @@ +[MASTER] +# Specify a configuration file. +rcfile=pylint.rc + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. Should be base names, not paths. +ignore=tests + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=4 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist=lxml.etree,greenlet + + + +[MESSAGES CONTROL] +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +# See "Messages Control" section of +# https://pylint.readthedocs.io/en/latest/user_guide +# We are disabling (C)onvention +# We are disabling (R)efactor +disable=C, R + +[REPORTS] +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + + +[SIMILARITIES] +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +[FORMAT] +# Maximum number of characters on a single line. +max-line-length=85 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually 4 spaces or "\t" (1 tab). +indent-string=' ' + + +[TYPECHECK] +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis +ignored-modules=distutils,eventlet.green.subprocess,six,six.moves + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +# pylint is confused by sqlalchemy Table, as well as sqlalchemy Enum types +# ie: (unprovisioned, identity) +# LookupDict in requests library confuses pylint +ignored-classes=SQLObject, optparse.Values, thread._local, _thread._local, + Table, unprovisioned, identity, LookupDict + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[BASIC] +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + + +[MISCELLANEOUS] +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[VARIABLES] +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[IMPORTS] +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + + +[EXCEPTIONS] +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/python-k8sapp-auditd/k8sapp_auditd/requirements.txt b/python-k8sapp-auditd/k8sapp_auditd/requirements.txt new file mode 100644 index 0000000..5bc15a1 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/requirements.txt @@ -0,0 +1,2 @@ +pbr>=0.5 +PyYAML==3.10 diff --git a/python-k8sapp-auditd/k8sapp_auditd/setup.cfg b/python-k8sapp-auditd/k8sapp_auditd/setup.cfg new file mode 100644 index 0000000..38bdb62 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/setup.cfg @@ -0,0 +1,42 @@ +[metadata] +name = k8sapp-auditd +summary = StarlingX sysinv extensions for auditd +long_description = file: README.rst +long_description_content_type = text/x-rst +license = Apache 2.0 +author = StarlingX +author-email = starlingx-discuss@lists.starlingx.io +home-page = https://www.starlingx.io/ +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 + +[files] +packages = + k8sapp_auditd + +[global] +setup-hooks = + pbr.hooks.setup_hook + +[entry_points] +systemconfig.helm_applications = + auditd = systemconfig.helm_plugins.auditd + +systemconfig.helm_plugins.auditd = + 001_auditd = k8sapp_auditd.helm.auditd:AuditdHelm + +systemconfig.armada.manifest_ops = + auditd = k8sapp_auditd.armada.manifest_auditd:AuditdArmadaManifestOperator + +[wheel] +universal = 1 diff --git a/python-k8sapp-auditd/k8sapp_auditd/setup.py b/python-k8sapp-auditd/k8sapp_auditd/setup.py new file mode 100644 index 0000000..28f9fe6 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/setup.py @@ -0,0 +1,12 @@ +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import setuptools + + +setuptools.setup( + setup_requires=['pbr>=2.0.0'], + pbr=True) diff --git a/python-k8sapp-auditd/k8sapp_auditd/test-requirements.txt b/python-k8sapp-auditd/k8sapp_auditd/test-requirements.txt new file mode 100644 index 0000000..685aa6e --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/test-requirements.txt @@ -0,0 +1,21 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +hacking>=1.1.0,<=2.0.0 # Apache-2.0 +astroid <= 2.2.5 +bandit;python_version>="3.0" +coverage>=3.6 +fixtures>=3.0.0 # Apache-2.0/BSD +mock>=2.0.0 # BSD +python-subunit>=0.0.18 +requests-mock>=0.6.0 # Apache-2.0 +sphinx +oslosphinx +oslotest>=3.2.0 # Apache-2.0 +stestr>=1.0.0 # Apache-2.0 +testrepository>=0.0.18 +testtools!=1.2.0,>=0.9.36 +isort<5;python_version>="3.0" +pylint<2.1.0;python_version<"3.0" # GPLv2 +pylint<2.4.0;python_version>="3.0" # GPLv2 +pycryptodomex diff --git a/python-k8sapp-auditd/k8sapp_auditd/tox.ini b/python-k8sapp-auditd/k8sapp_auditd/tox.ini new file mode 100644 index 0000000..9e870d8 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/tox.ini @@ -0,0 +1,136 @@ +[tox] +envlist = flake8,py27,py36,pylint,bandit +minversion = 1.6 +# skipsdist = True +#,pip-missing-reqs + +# tox does not work if the path to the workdir is too long, so move it to /tmp +toxworkdir = /tmp/{env:USER}_k8saudittox +stxdir = {toxinidir}/../../.. +distshare={toxworkdir}/.tox/distshare + +[testenv] +# usedevelop = True +# enabling usedevelop results in py27 develop-inst: +# Exception: Versioning for this project requires either an sdist tarball, +# or access to an upstream git repository. +# Note. site-packages is true and rpm-python must be yum installed on your dev machine. +sitepackages = True +basepython = python3 + +# tox is silly... these need to be separated by a newline.... +whitelist_externals = bash + find + +install_command = pip install --use-deprecated legacy-resolver \ + -v -v -v \ + -c{toxinidir}/upper-constraints.txt \ + -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/stable/stein/upper-constraints.txt} \ + {opts} {packages} + +# Note the hash seed is set to 0 until can be tested with a +# random hash seed successfully. +setenv = VIRTUAL_ENV={envdir} + PYTHONHASHSEED=0 + PYTHONDONTWRITEBYTECODE=1 + OS_TEST_PATH=./k8sapp_auditd/tests + LANG=en_US.UTF-8 + LANGUAGE=en_US:en + LC_ALL=C + EVENTS_YAML=./k8sapp_auditd/tests/events_for_testing.yaml + SYSINV_TEST_ENV=True + TOX_WORK_DIR={toxworkdir} + PYLINTHOME={toxworkdir} + +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + -e{[tox]stxdir}/config/sysinv/sysinv/sysinv + -e{[tox]stxdir}/config/tsconfig/tsconfig + -e{[tox]stxdir}/fault/fm-api + -e{[tox]stxdir}/fault/python-fmclient/fmclient + -e{[tox]stxdir}/update/cgcs-patch/cgcs-patch + -e{[tox]stxdir}/utilities/ceph/python-cephclient/python-cephclient + +commands = + find . -type f -name "*.pyc" -delete + +[flake8] +ignore = H101,H102,H104,H105,H306,H401,H403,H404,H405,H701,H702,H703, + B006,B007,B009,B010,B012,B014,B301,B306, + W503,W504,W605, + E117,E126,E127,E128,E402 +exclude = build,dist,tools,.eggs +max-line-length=120 + +[testenv:flake8] +basepython = python3 +deps = -r{toxinidir}/test-requirements.txt + flake8-bugbear +commands = + flake8 {posargs} . + +[testenv:py27] +basepython = python2.7 +commands = + {[testenv]commands} + stestr run {posargs} + stestr slowest + +[testenv:py36] +basepython = python3.6 +commands = + {[testenv]commands} + stestr run {posargs} + stestr slowest + +[testenv:pep8] +# testenv:flake8 clone +basepython = python3 +deps = {[testenv:flake8]deps} +commands = {[testenv:flake8]commands} + +[testenv:venv] +commands = {posargs} + +[bandit] +skips = B101,B103,B104,B105,B108,B110,B303,B307,B310,B311,B314,B318,B320,B404,B405,B408,B410,B506,B602,B603,B604,B605,B607 +exclude = tests + +[testenv:bandit] +basepython = python3 +deps = -r{toxinidir}/test-requirements.txt + bandit + +commands = bandit --ini tox.ini -n 5 -r k8sapp_auditd + +[testenv:pylint] +basepython = python3.6 +sitepackages = False + +deps = {[testenv]deps} +commands = + pylint {posargs} k8sapp_auditd --rcfile=./pylint.rc + +[testenv:cover] +basepython = python2.7 +deps = {[testenv]deps} +setenv = {[testenv]setenv} + PYTHON=coverage run --parallel-mode + +commands = + {[testenv]commands} + coverage erase + stestr run {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml + coverage report + +[testenv:pip-missing-reqs] +# do not install test-requirements as that will pollute the virtualenv for +# determining missing packages +# this also means that pip-missing-reqs must be installed separately, outside +# of the requirements.txt files +deps = pip_missing_reqs + -rrequirements.txt +commands=pip-missing-reqs -d --ignore-file=/k8sapp_auditd/tests k8sapp_auditd diff --git a/python-k8sapp-auditd/k8sapp_auditd/upper-constraints.txt b/python-k8sapp-auditd/k8sapp_auditd/upper-constraints.txt new file mode 100644 index 0000000..9c30188 --- /dev/null +++ b/python-k8sapp-auditd/k8sapp_auditd/upper-constraints.txt @@ -0,0 +1 @@ +# Override upstream constraints based on StarlingX load diff --git a/stx-audit-helm/centos/build_srpm.data b/stx-audit-helm/centos/build_srpm.data index 2d78bc1..1422f18 100644 --- a/stx-audit-helm/centos/build_srpm.data +++ b/stx-audit-helm/centos/build_srpm.data @@ -1,4 +1,6 @@ SRC_DIR="stx-audit-helm" +OPT_DEP_LIST="$STX_BASE/audit-armada-app/python-k8sapp-auditd" -TIS_PATCH_VER=PKG_GITREVCOUNT +TIS_PATCH_VER=GITREVCOUNT +TIS_BASE_SRCREV=eeb94bddc8c4c6e513adfae5505e174e0445deed diff --git a/stx-audit-helm/centos/docker/stx-audit/Dockerfile b/stx-audit-helm/centos/docker/stx-audit/Dockerfile index 12d1506..29934a2 100644 --- a/stx-audit-helm/centos/docker/stx-audit/Dockerfile +++ b/stx-audit-helm/centos/docker/stx-audit/Dockerfile @@ -5,5 +5,10 @@ RUN yum -y install\ audit COPY startAuditd.sh . + +RUN mkdir -p /etc/audit +RUN touch /etc/audit/audit.rules +RUN chmod 600 /etc/audit/audit.rules + RUN chmod 755 startAuditd.sh ENTRYPOINT ["./startAuditd.sh"] \ No newline at end of file diff --git a/stx-audit-helm/centos/docker/stx-audit/startAuditd.sh b/stx-audit-helm/centos/docker/stx-audit/startAuditd.sh index cd072d6..b0e0822 100644 --- a/stx-audit-helm/centos/docker/stx-audit/startAuditd.sh +++ b/stx-audit-helm/centos/docker/stx-audit/startAuditd.sh @@ -10,6 +10,10 @@ set -u echo "Starting auditd …" +# update /etc/audit/audit.rules with any changes made to +# /etc/audit/rules.d/audit.rules +augenrules --load + # start auditd with no fork to run in the background in the container /sbin/auditd -n -l EXIT_STATUS=$? diff --git a/stx-audit-helm/centos/stx-audit-helm.spec b/stx-audit-helm/centos/stx-audit-helm.spec index 337ac5d..23c161f 100644 --- a/stx-audit-helm/centos/stx-audit-helm.spec +++ b/stx-audit-helm/centos/stx-audit-helm.spec @@ -25,6 +25,8 @@ Source0: %{name}-%{version}.tar.gz BuildArch: noarch BuildRequires: helm +BuildRequires: python-k8sapp-auditd +BuildRequires: python-k8sapp-auditd-wheels %description StarlingX AUDITD Helm Charts @@ -55,6 +57,10 @@ sed -i 's/@APP_NAME@/%{app_name}/g' %{app_staging}/metadata.yaml sed -i 's/@APP_VERSION@/%{version}-%{tis_patch_ver}/g' %{app_staging}/metadata.yaml sed -i 's/@HELM_REPO@/%{helm_repo}/g' %{app_staging}/metadata.yaml +# Copy the plugins: installed in the buildroot +mkdir -p %{app_staging}/plugins +cp /plugins/%{app_name}/*.whl %{app_staging}/plugins + # package it up find . -type f ! -name '*.md5' -print0 | xargs -0 md5sum > checksum.md5 tar -zcf %{_builddir}/%{app_tarball} -C %{app_staging}/ . diff --git a/stx-audit-helm/stx-audit-helm/helm-charts/audit/auditd/auditd.conf b/stx-audit-helm/stx-audit-helm/helm-charts/audit/auditd/auditd.conf deleted file mode 100755 index 05374b6..0000000 --- a/stx-audit-helm/stx-audit-helm/helm-charts/audit/auditd/auditd.conf +++ /dev/null @@ -1,36 +0,0 @@ -# -# This file controls the configuration of the auditd daemon -# -local_events = yes -write_logs = yes -log_file = /var/log/audit/audit.log -log_group = root -log_format = RAW -flush = INCREMENTAL_ASYNC -freq = 50 -max_log_file = 8 -num_logs = 5 -priority_boost = 4 -disp_qos = lossy -dispatcher = /sbin/audispd -name_format = NONE -##name = mydomain -max_log_file_action = ROTATE -space_left = 75 -space_left_action = SYSLOG -##verify_email = yes -##action_mail_acct = root -admin_space_left = 50 -admin_space_left_action = SYSLOG -disk_full_action = SYSLOG -disk_error_action = SYSLOG -use_libwrap = yes -##tcp_listen_port = 60 -##tcp_listen_queue = 5 -##tcp_max_per_addr = 1 -##tcp_client_ports = 1024-65535 -##tcp_client_max_idle = 0 -enable_krb5 = no -krb5_principal = auditd -##krb5_key_file = /etc/audit/audit.key -distribute_network = no diff --git a/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/configmap.yaml b/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/configmap.yaml index 47193e6..400ca93 100755 --- a/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/configmap.yaml +++ b/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/configmap.yaml @@ -4,3 +4,10 @@ metadata: name: {{ .Values.configmap.name_auditd }} data: auditd.conf: {{ toYaml .Values.auditdconf | indent 2 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.configmap.name_auditd_rules }} +data: + audit.rules: {{ toYaml .Values.auditdrules | indent 2 }} diff --git a/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/daemonset.yaml b/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/daemonset.yaml index 983841e..b932258 100644 --- a/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/daemonset.yaml +++ b/stx-audit-helm/stx-audit-helm/helm-charts/audit/templates/daemonset.yaml @@ -2,17 +2,22 @@ apiVersion: apps/v1 kind: DaemonSet metadata: name: {{ include "audit.fullname" . }} - namespace: kube-system labels: - k8s-app: auditd-logging + app: {{ include "audit.name" . }} + chart: {{ include "audit.chart" . }} + release: {{ .Release.Name }} spec: selector: matchLabels: - name: {{ include "audit.name" . }} + app: {{ include "audit.name" . }} + release: {{ .Release.Name }} template: metadata: labels: - name: {{ include "audit.name" . }} + app: {{ include "audit.name" . }} + release: {{ .Release.Name }} + annotations: + rollme: {{ randAlphaNum 1000 | quote }} spec: hostNetwork: true hostPID: true @@ -27,11 +32,28 @@ spec: volumeMounts: - name: varlog mountPath: /var/log/audit + - name: auditd-etc-config-vol + mountPath: /etc/audit/auditd.conf + subPath: auditd.conf + - name: auditd-etc-rules-vol + mountPath: /etc/audit/rules.d stdin: true tty: true volumes: - name: varlog hostPath: path: /var/log/audit + - name: auditd-etc-config-vol + configMap: + name: {{ .Values.configmap.name_auditd }} + items: + - key: "auditd.conf" + path: "auditd.conf" + - name: auditd-etc-rules-vol + configMap: + name: {{ .Values.configmap.name_auditd_rules }} + items: + - key: audit.rules + path: audit.rules imagePullSecrets: - name: default-registry-key diff --git a/stx-audit-helm/stx-audit-helm/helm-charts/audit/values.yaml b/stx-audit-helm/stx-audit-helm/helm-charts/audit/values.yaml index a677a7b..e7d9392 100755 --- a/stx-audit-helm/stx-audit-helm/helm-charts/audit/values.yaml +++ b/stx-audit-helm/stx-audit-helm/helm-charts/audit/values.yaml @@ -13,6 +13,7 @@ fullnameOverride: "" configmap: name_auditd: auditd-etc-config + name_auditd_rules: auditd-etc-rules auditdconf: |- ########################################################################## @@ -53,3 +54,14 @@ auditdconf: |- krb5_principal = auditd ##krb5_key_file = /etc/audit/audit.key distribute_network = no + +auditdrules: |- + ## First rule - delete all + -D + + ## Increase the buffers to survive stress events. + ## Make this bigger for busy systems + -b 8192 + + ## Set failure mode to syslog + -f 1 diff --git a/tox.ini b/tox.ini index 6eeb005..807555b 100644 --- a/tox.ini +++ b/tox.ini @@ -48,3 +48,12 @@ commands = [testenv:flake8] basepython = python3 description = Dummy environment to allow flake8 to be run in subdir tox + +[testenv:pylint] +basepython = python3 +description = Dummy environment to allow pylint to be run in subdir tox + +[testenv:bandit] +basepython = python3 +description = Dummy environment to allow bandit to be run in subdir tox +