From 99dc3a158210ec749b9186471a2b301e4031a3c0 Mon Sep 17 00:00:00 2001 From: Aleksandr Iantsen Date: Wed, 18 Sep 2024 10:49:35 +0300 Subject: [PATCH 1/3] added support of SSL connection configuration for Zabbix API --- .github/configs/default.conf | 12 +- .github/configs/nginx.cnf | 19 +++ .github/configs/nginx.crt | 23 --- .github/configs/nginx.key | 28 ---- .github/scripts/additional_api_tests.py | 136 +++++++++++++++++- .github/scripts/library_import_tests.sh | 12 +- .github/workflows/additional_tests.yaml | 9 +- .../api/asynchronous/custom_ssl_context.py | 62 ++++++++ .../api/synchronous/custom_ssl_context.py | 36 +++++ tests/common.py | 8 ++ zabbix_utils/aioapi.py | 4 +- zabbix_utils/api.py | 12 +- 12 files changed, 295 insertions(+), 66 deletions(-) create mode 100644 .github/configs/nginx.cnf delete mode 100644 .github/configs/nginx.crt delete mode 100644 .github/configs/nginx.key create mode 100644 examples/api/asynchronous/custom_ssl_context.py create mode 100644 examples/api/synchronous/custom_ssl_context.py diff --git a/.github/configs/default.conf b/.github/configs/default.conf index 6c92c5b..3548a6e 100644 --- a/.github/configs/default.conf +++ b/.github/configs/default.conf @@ -4,11 +4,11 @@ server { root /var/www/html/; index index.php; - server_name 127.0.0.1; + server_name localhost 127.0.0.1; ssl_certificate /etc/nginx/ssl/nginx.crt; ssl_certificate_key /etc/nginx/ssl/nginx.key; - location / { + location /http_auth/ { auth_basic "Zabbix HTTP Auth"; auth_basic_user_file /etc/nginx/.htpasswd; @@ -18,4 +18,12 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering on; } + + location /ssl_context/ { + proxy_pass http://localhost:8080/; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_buffering on; + } } \ No newline at end of file diff --git a/.github/configs/nginx.cnf b/.github/configs/nginx.cnf new file mode 100644 index 0000000..83b8ab0 --- /dev/null +++ b/.github/configs/nginx.cnf @@ -0,0 +1,19 @@ +[req] +default_bits = 2048 +distinguished_name = req_distinguished_name +req_extensions = req_ext +x509_extensions = v3_req +prompt = no +[req_distinguished_name] +countryName = LV +localityName = Riga +organizationName = Zabbix SIA +organizationalUnitName = Integration team +emailAddress = integrationteam@zabbix.com +[req_ext] +subjectAltName = @alt_names +[v3_req] +subjectAltName = @alt_names +[alt_names] +DNS.1 = localhost +IP.1 = 127.0.0.1 \ No newline at end of file diff --git a/.github/configs/nginx.crt b/.github/configs/nginx.crt deleted file mode 100644 index 3d2b137..0000000 --- a/.github/configs/nginx.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDzzCCAregAwIBAgIUfvqWoC2rRssyrQe3JruyCkQFOpYwDQYJKoZIhvcNAQEL -BQAwdzELMAkGA1UEBhMCTFYxDTALBgNVBAgMBFJpZ2ExEzARBgNVBAoMClphYmJp -eCBTSUExGTAXBgNVBAsMEEludGVncmF0aW9uIHRlYW0xKTAnBgkqhkiG9w0BCQEW -GmludGVncmF0aW9udGVhbUB6YWJiaXguY29tMB4XDTIzMTEwNzEwMzMwOVoXDTI0 -MTEwNjEwMzMwOVowdzELMAkGA1UEBhMCTFYxDTALBgNVBAgMBFJpZ2ExEzARBgNV -BAoMClphYmJpeCBTSUExGTAXBgNVBAsMEEludGVncmF0aW9uIHRlYW0xKTAnBgkq -hkiG9w0BCQEWGmludGVncmF0aW9udGVhbUB6YWJiaXguY29tMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuW+v+fg7XbldU86WLgZSDDDEaFMZ8GWD5n2l -zJC9gj/pzK6jnPMzWib2dPbH4zHxXGIxToBXut388hiDsGvsM4KZvtf/eFZAsR/E -zMEjgPpv+4ys0ZvDZ2KfHeQBnR06uOYTLw9wqxFmGGYwC6tbaF81fQ4GTYx47Syd -pAof5dlCdjfotombvRE7sbavkoctxKznBsF8LLfmxWzbqc2eZgLfglfDoCTINF0f -pVWt+K2kcSvs1jlBREU9EQsJiJa5QFv0AyK6jRDZFx4DN0wyzAIRcPMHbBZLbMp0 -sNpRP+CYgBtF9uO9HsFQHIIedfL/5N0cOKIFSNyj707RlUELNwIDAQABo1MwUTAd -BgNVHQ4EFgQUbjxYf/ZnBC61ey0UBscYd2dbNR0wHwYDVR0jBBgwFoAUbjxYf/Zn -BC61ey0UBscYd2dbNR0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC -AQEAfYOByo8LdlRafVyi1w5VW1oA9Rc+9TqnRp6PEPSF8UqP6FH32YNpIVoWpz9z -3IMLsroVRMtxLzplBvq4muqFFTLXR4ZUi09arVxIASRmXyM1Fx0wd/ncontNS8zF -SKG+nam6+IHKC2gFq/KkzH1/o1T0d7RBuVMSSeLfBNjkVGBbueWmS2B+HquLG7uk -S/K1/YHF8bI+FIRJoQZtemX9h+MtBGmSNUsIhFWiL7EIPaxXnLgLhgSCBEJL8Zi/ -3ylq9vOIe0vSPdUt2TzZq622D4vkMZsHP5gnt/nao7ncE6QvGUCYNJ6JeF+vScSy -nyxCJk4/jJ0IMsLn7g+ylfonnA== ------END CERTIFICATE----- diff --git a/.github/configs/nginx.key b/.github/configs/nginx.key deleted file mode 100644 index a8cf18d..0000000 --- a/.github/configs/nginx.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5b6/5+DtduV1T -zpYuBlIMMMRoUxnwZYPmfaXMkL2CP+nMrqOc8zNaJvZ09sfjMfFcYjFOgFe63fzy -GIOwa+wzgpm+1/94VkCxH8TMwSOA+m/7jKzRm8NnYp8d5AGdHTq45hMvD3CrEWYY -ZjALq1toXzV9DgZNjHjtLJ2kCh/l2UJ2N+i2iZu9ETuxtq+Shy3ErOcGwXwst+bF -bNupzZ5mAt+CV8OgJMg0XR+lVa34raRxK+zWOUFERT0RCwmIlrlAW/QDIrqNENkX -HgM3TDLMAhFw8wdsFktsynSw2lE/4JiAG0X2470ewVAcgh518v/k3Rw4ogVI3KPv -TtGVQQs3AgMBAAECggEAGHSNTbcaBTNEBqBxF+LHgUoRLP0Ap76LlaMucXsJMxtA -tzi05ScBt7r5t9Uv6VuVzCw6+nJcn41VJYxsGaEyBl0L76pGy9T8NR/OcX5rM0BT -8mXMCNy0+XZ9K5D9UX4gV3AoSYWMw1LJFyCq528S4AHmaPaniCSlcgn3REH9kO9L -oW0iDYHV57Sn+VJc2YBzu4e+i+BMmmXzuV58eExsgYgM6pZ3CAtHIIFsRGe1eZva -GX/iQJLYBswthgULlzeehEye0GIXFEQmo0E0y5+DdxNllQPFy1er4Jxbcc9IITjL -kYYHIo2iZFCVLL/8pydiAo3pt6EIcaZ+PvAKO08a4QKBgQDTrUvEgY5MOJrHYCxA -phQVsFe9r+FX/5OilWaW6nt4MsQaDt9Owu7VJMv366aj6/6WFgUzjmuYWPc7vDec -jwur8Zy7Ku6EoP3ebZqqL0YHkLwnyXcMMuI19zzLZBNws+TQI6U7bVg2ZK0npcok -WlT6+tA8ZZVUmrB1dIO8RSzWpwKBgQDgQ8pcdvoVgbiall27jYpPBhnrp0vxCF4b -/BDghZiPaM9jqKA/HpnhS62RHbV/9Gp3YDd3Tf2vRLCZNqgTEoGNUyf7eSDxwetD -MBumIrO0vJhiEwWKC2XtOk7HHyQ9NfienjpeSJi89h4vPJnIbpwPOUE0IndoECe9 -4j7FenRI8QKBgGeFYf+paKEoMbH9+8ZmMn5k7pQrFubFbDtgG3sFE/s1wKgJy/Yl -6U2+IC6QRb/3I1vdSw/qv/4SzF6fbwcXAhtGH2N93IZqgZ2tNq2yTupWQ16l7Z2+ -qNjBoC7MM6GzqNyOAjntAO4hEHnKX/ClaNgRn5NaDs0AlfnrUJ3bqtETAoGAGiPs -t4BqwA0BzosOW+kwoOtuJAi5ElxOU+a6tOD9mmG2IsCqBRF1EEy463xSBpsfudBu -RqrBkidoikNTrf1vOmDh/S7uRx+6Y3d9EY1j8wI0pOHguGmBtPgh7JXVrIjY9Owu -k4TUwNnTROBEcJdr+15KGmCWGHwhlcGEXZJbcZECgYEAgF863LUJRFjT+bW59oA/ -B3S9YKZ1QG5Pr6lH4VvzILU8wCMVV58M1Y54iySbJ8QB1vaO3ethKxNveRdTOrAp -1w/92ty9WqdKBoRqUqeiuA/+sgP+OOiFM0cUEPg1aYIVAuHLvyHr+zLVfJ9mqgEo -5MuvCiPeJbPgNBtOzuL5GF8= ------END PRIVATE KEY----- diff --git a/.github/scripts/additional_api_tests.py b/.github/scripts/additional_api_tests.py index 08bdebb..1ffe373 100644 --- a/.github/scripts/additional_api_tests.py +++ b/.github/scripts/additional_api_tests.py @@ -5,8 +5,10 @@ # See the LICENSE file in the project root for more information. import sys +import ssl import base64 import unittest +from aiohttp import ClientSession, TCPConnector sys.path.append('.') from zabbix_utils.api import ZabbixAPI @@ -24,9 +26,9 @@ class IntegrationAPITest(unittest.TestCase): """Test working with a real Zabbix API instance synchronously""" def setUp(self): - self.url = ZABBIX_URL self.user = ZABBIX_USER self.password = ZABBIX_PASSWORD + self.url = ZABBIX_URL + '/http_auth/' self.api = ZabbixAPI( url=self.url, user=self.user, @@ -89,13 +91,76 @@ def test_user_get(self): self.assertEqual(type(users), list, "Request user.get was going wrong") +class CustomCertAPITest(unittest.TestCase): + """Test working with a real Zabbix API instance synchronously""" + + def setUp(self): + self.user = ZABBIX_USER + self.password = ZABBIX_PASSWORD + self.url = ZABBIX_URL + '/ssl_context/' + + context = ssl.create_default_context() + context.load_verify_locations('/etc/nginx/ssl/nginx.crt') + + self.api = ZabbixAPI( + url=self.url, + user=self.user, + password=self.password, + skip_version_check=True, + ssl_context=context + ) + + def tearDown(self): + if self.api: + self.api.logout() + + def test_login(self): + """Tests login function works properly""" + + self.assertEqual( + type(self.api), ZabbixAPI, "Login was going wrong") + self.assertEqual( + type(self.api.api_version()), APIVersion, "Version getting was going wrong") + + def test_version_get(self): + """Tests getting version info works properly""" + + version = None + if self.api: + version = self.api.apiinfo.version() + self.assertEqual( + version, str(self.api.api_version()), "Request apiinfo.version was going wrong") + + def test_check_auth(self): + """Tests checking authentication state works properly""" + + resp = None + if self.api: + if self.api._ZabbixAPI__session_id == self.api._ZabbixAPI__token: + resp = self.api.user.checkAuthentication(token=self.api._ZabbixAPI__session_id) + else: + resp = self.api.user.checkAuthentication(sessionid=self.api._ZabbixAPI__session_id) + self.assertEqual( + type(resp), dict, "Request user.checkAuthentication was going wrong") + + def test_user_get(self): + """Tests getting users info works properly""" + + users = None + if self.api: + users = self.api.user.get( + output=['userid', 'name'] + ) + self.assertEqual(type(users), list, "Request user.get was going wrong") + + class IntegrationAsyncAPITest(unittest.IsolatedAsyncioTestCase): """Test working with a real Zabbix API instance asynchronously""" async def asyncSetUp(self): - self.url = ZABBIX_URL self.user = ZABBIX_USER self.password = ZABBIX_PASSWORD + self.url = ZABBIX_URL + '/http_auth/' self.api = AsyncZabbixAPI( url=self.url, skip_version_check=True, @@ -163,5 +228,72 @@ async def test_user_get(self): self.assertEqual(type(users), list, "Request user.get was going wrong") +class CustomCertAsyncAPITest(unittest.IsolatedAsyncioTestCase): + """Test working with a real Zabbix API instance asynchronously""" + + async def asyncSetUp(self): + self.user = ZABBIX_USER + self.password = ZABBIX_PASSWORD + self.url = ZABBIX_URL + '/ssl_context/' + + context = ssl.create_default_context() + context.load_verify_locations('/etc/nginx/ssl/nginx.crt') + session = ClientSession( + connector=TCPConnector(ssl=context) + ) + + self.api = AsyncZabbixAPI( + url=self.url, + skip_version_check=True, + client_session=session + ) + await self.api.login( + user=self.user, + password=self.password + ) + + async def asyncTearDown(self): + if self.api: + await self.api.logout() + + async def test_login(self): + """Tests login function works properly""" + + self.assertEqual( + type(self.api), AsyncZabbixAPI, "Login was going wrong") + self.assertEqual( + type(self.api.api_version()), APIVersion, "Version getting was going wrong") + + async def test_version_get(self): + """Tests getting version info works properly""" + + version = None + if self.api: + version = await self.api.apiinfo.version() + self.assertEqual( + version, str(self.api.api_version()), "Request apiinfo.version was going wrong") + + async def test_check_auth(self): + """Tests checking authentication state works properly""" + + resp = None + if self.api: + if self.api._AsyncZabbixAPI__session_id == self.api._AsyncZabbixAPI__token: + resp = await self.api.user.checkAuthentication(token=(self.api._AsyncZabbixAPI__session_id or '')) + else: + resp = await self.api.user.checkAuthentication(sessionid=(self.api._AsyncZabbixAPI__session_id or '')) + self.assertEqual( + type(resp), dict, "Request user.checkAuthentication was going wrong") + + async def test_user_get(self): + """Tests getting users info works properly""" + + users = None + if self.api: + users = await self.api.user.get( + output=['userid', 'name'] + ) + self.assertEqual(type(users), list, "Request user.get was going wrong") + if __name__ == '__main__': unittest.main() diff --git a/.github/scripts/library_import_tests.sh b/.github/scripts/library_import_tests.sh index 1de8649..e505801 100644 --- a/.github/scripts/library_import_tests.sh +++ b/.github/scripts/library_import_tests.sh @@ -1,7 +1,13 @@ #!/bin/bash -class=$1 -error=$2 +mode=$1 +class=$2 +error=$3 -result=$(python3 -c "import sys; sys.path.append('.'); from zabbix_utils import $class; $class()" 2>&1) +cmd="import sys; sys.path.append('.'); from zabbix_utils import $class; $class()" +if [ $mode == "async" ]; then + cmd="import sys; import asyncio; sys.path.append('.'); from zabbix_utils import $class; exec('async def main():\n $class()'); asyncio.run(main())" +fi + +result=$(python3 -c "$cmd" 2>&1) echo "$result" | grep "$error" >/dev/null || echo "$result" | (python3 "./.github/scripts/telegram_msg.py" && echo "Error") diff --git a/.github/workflows/additional_tests.yaml b/.github/workflows/additional_tests.yaml index e83a1b2..be6bc60 100644 --- a/.github/workflows/additional_tests.yaml +++ b/.github/workflows/additional_tests.yaml @@ -33,7 +33,7 @@ jobs: TBOT_CHAT: ${{ vars.TBOT_CHAT }} SUBJECT: Importing test without requirements FAIL run: | - bash ./.github/scripts/library_import_tests.sh "ZabbixAPI" "Unable to connect to" > /tmp/importing.log + bash ./.github/scripts/library_import_tests.sh "sync" "ZabbixAPI" "Unable to connect to" > /tmp/importing.log - name: Check import of async without requirements continue-on-error: true env: @@ -41,7 +41,7 @@ jobs: TBOT_CHAT: ${{ vars.TBOT_CHAT }} SUBJECT: Importing test without requirements FAIL run: | - bash ./.github/scripts/library_import_tests.sh "AsyncZabbixAPI" "ModuleNotFoundError:" > /tmp/importing.log + bash ./.github/scripts/library_import_tests.sh "async" "AsyncZabbixAPI" "ModuleNotFoundError:" > /tmp/importing.log - name: Install requirements run: | pip install -r ./requirements.txt @@ -52,7 +52,7 @@ jobs: TBOT_CHAT: ${{ vars.TBOT_CHAT }} SUBJECT: Importing tests with requirements FAIL run: | - bash ./.github/scripts/library_import_tests.sh "AsyncZabbixAPI" "aiohttp.client.ClientSession" > /tmp/importing.log + bash ./.github/scripts/library_import_tests.sh "async" "AsyncZabbixAPI" "aiohttp.client.ClientSession" > /tmp/importing.log - name: Raise an exception run: | test $(cat /tmp/importing.log | wc -l) -eq 0 || exit 1 @@ -77,9 +77,8 @@ jobs: echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf sudo mkdir -p /etc/nginx/ssl/ sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/.htpasswd /etc/nginx/.htpasswd - sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/nginx.crt /etc/nginx/ssl/nginx.crt - sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/nginx.key /etc/nginx/ssl/nginx.key sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/default.conf /etc/nginx/sites-enabled/default + sudo openssl req -x509 -nodes -days 1 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt -config $WORKDIR/${{ env.CONFIG_PATH }}/nginx.cnf sudo chown -R www-data:www-data /etc/nginx/ cd ui sudo rm /var/www/html/index.html diff --git a/examples/api/asynchronous/custom_ssl_context.py b/examples/api/asynchronous/custom_ssl_context.py new file mode 100644 index 0000000..70cddfc --- /dev/null +++ b/examples/api/asynchronous/custom_ssl_context.py @@ -0,0 +1,62 @@ +# Copyright (C) 2001-2023 Zabbix SIA +# +# Zabbix SIA licenses this file to you under the MIT License. +# See the LICENSE file in the project root for more information. + +import ssl +import asyncio +from zabbix_utils import AsyncZabbixAPI +from aiohttp import ClientSession, TCPConnector + + +# Zabbix server URL or IP address +ZABBIX_SERVER = "https://example.com" + +# Zabbix server authentication credentials +ZABBIX_AUTH = { + "user": "Admin", # Zabbix user name for authentication + "password": "zabbix" # Zabbix user password for authentication +} + + +async def main(): + """ + The main function to perform asynchronous tasks. + """ + + # Create a default SSL context for secure connections + # Load a custom certificate from the specified file path to verify the server + ctx = ssl.create_default_context() + ctx.load_verify_locations("/path/to/certificate.crt") + + # Create an asynchronous client session for HTTP requests + client_session = ClientSession( + connector=TCPConnector(ssl=ctx) + ) + + # Create an instance of the AsyncZabbixAPI class + api = AsyncZabbixAPI( + url=ZABBIX_SERVER, + client_session=client_session + ) + + # Authenticating with Zabbix API using the provided token. + await api.login(**ZABBIX_AUTH) + + # Retrieve a list of hosts from the Zabbix server, including their host ID and name + hosts = await api.host.get( + output=['hostid', 'name'] + ) + + # Print the names of the retrieved hosts + for host in hosts: + print(host['name']) + + # Logout to release the Zabbix API session + await api.logout() + + # Close asynchronous client session + await client_session.close() + +# Run the main coroutine +asyncio.run(main()) diff --git a/examples/api/synchronous/custom_ssl_context.py b/examples/api/synchronous/custom_ssl_context.py new file mode 100644 index 0000000..91f7fb4 --- /dev/null +++ b/examples/api/synchronous/custom_ssl_context.py @@ -0,0 +1,36 @@ +# Copyright (C) 2001-2023 Zabbix SIA +# +# Zabbix SIA licenses this file to you under the MIT License. +# See the LICENSE file in the project root for more information. + +import ssl +from zabbix_utils import ZabbixAPI + +# Create a default SSL context for secure connections +# Load a custom certificate from the specified file path to verify the server +ctx = ssl.create_default_context() +ctx.load_verify_locations("/path/to/certificate.crt") + +# Create an instance of the ZabbixAPI class with the Zabbix server URL +# Set ssl_context value for Zabbix API requests. +ZABBIX_AUTH = { + "url": "https://example.com", + "user": "Admin", + "password": "zabbix", + "ssl_context": ctx +} + +# Login to the Zabbix API using provided user credentials +api = ZabbixAPI(**ZABBIX_AUTH) + +# Retrieve a list of hosts from the Zabbix server, including their host ID and name +hosts = api.host.get( + output=['hostid', 'name'] +) + +# Print the names of the retrieved hosts +for host in hosts: + print(host['name']) + +# Logout to release the Zabbix API session +api.logout() diff --git a/tests/common.py b/tests/common.py index 4446acf..cab5887 100644 --- a/tests/common.py +++ b/tests/common.py @@ -78,15 +78,23 @@ class MockBasicAuth(): login = API_DEFAULTS['user'] password = API_DEFAULTS['password'] +class MockSessionConn(): + def __init__(self): + self._ssl = None class MockSession(): def __init__(self, exception=None): self._default_auth = None + self._connector = MockSessionConn() self.EXC = exception def set_auth(self): self._default_auth = MockBasicAuth() def del_auth(self): self._default_auth = None + def set_ssl(self, ssl): + self._connector._ssl = ssl + def del_ssl(self): + self._connector._ssl = None def set_exception(self, exception): self.EXC = exception def del_exception(self): diff --git a/zabbix_utils/aioapi.py b/zabbix_utils/aioapi.py index 9d2bbf8..97290ad 100644 --- a/zabbix_utils/aioapi.py +++ b/zabbix_utils/aioapi.py @@ -34,7 +34,7 @@ from os import environ as env from urllib.error import URLError -from typing import Callable, Union, Optional, Any, List +from typing import Callable, Union, Optional, Any from aiohttp.client_exceptions import ContentTypeError from .types import APIVersion @@ -420,6 +420,8 @@ def send_sync_request(self, method: str, params: Optional[dict] = None, ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE + elif not isinstance(self.client_session._connector._ssl, bool): + ctx = self.client_session._connector._ssl else: ctx = None diff --git a/zabbix_utils/api.py b/zabbix_utils/api.py index c440262..1039757 100644 --- a/zabbix_utils/api.py +++ b/zabbix_utils/api.py @@ -33,7 +33,7 @@ from os import environ as env from urllib.error import URLError -from typing import Callable, Union, Optional, Any +from typing import Callable, Optional, Any from .types import APIVersion from .common import ModuleUtils @@ -119,7 +119,8 @@ class ZabbixAPI(): def __init__(self, url: Optional[str] = None, token: Optional[str] = None, user: Optional[str] = None, password: Optional[str] = None, http_user: Optional[str] = None, http_password: Optional[str] = None, - skip_version_check: bool = False, validate_certs: bool = True, timeout: int = 30): + skip_version_check: bool = False, validate_certs: bool = True, + ssl_context: Optional[ssl.SSLContext] = None, timeout: int = 30): url = url or env.get('ZABBIX_URL') or 'http://localhost/zabbix/api_jsonrpc.php' user = user or env.get('ZABBIX_USER') or None @@ -133,6 +134,11 @@ def __init__(self, url: Optional[str] = None, token: Optional[str] = None, if http_user and http_password: self.__basic_auth(http_user, http_password) + if ssl_context is not None: + if not isinstance(ssl_context, ssl.SSLContext): + raise TypeError('Function "ssl_context" must return "ssl.SSLContext".') from None + self.ssl_context = ssl_context + self.__check_version(skip_version_check) if token or user or password: @@ -337,6 +343,8 @@ def send_api_request(self, method: str, params: Optional[dict] = None, ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE + elif self.ssl_context is not None: + ctx = self.ssl_context else: ctx = None From 02bb86c86ab134d5d02942c960ee391af72bcd55 Mon Sep 17 00:00:00 2001 From: Aleksandr Iantsen Date: Wed, 18 Sep 2024 10:50:06 +0300 Subject: [PATCH 2/3] updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c91adaf..d891ad0 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ $ git clone https://github.com/zabbix/python-zabbix-utils Install **zabbix_utils** library using setup.py: ```bash -$ cd python-zabbix-utils-master/ +$ cd python-zabbix-utils/ $ python3 setup.py install ``` From ba152b8561eb62e6084e6f4d24bdeb1e67b48bca Mon Sep 17 00:00:00 2001 From: Aleksandr Iantsen Date: Wed, 18 Sep 2024 10:51:06 +0300 Subject: [PATCH 3/3] updated CHANGELOG and version number --- CHANGELOG.md | 7 +++++++ zabbix_utils/version.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60b7e82..a068122 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [2.0.1](https://github.com/zabbix/python-zabbix-utils/compare/v2.0.0...v2.0.1) (2024-09-18) + +### Features: + +- added ssl_context argument to ZabbixAPI to allow more flexible configuration of SSL connections +- added support of SSL connection configuration to AsyncZabbixAPI + ## [2.0.0](https://github.com/zabbix/python-zabbix-utils/compare/v1.1.1...v2.0.0) (2024-04-12) ### Features: diff --git a/zabbix_utils/version.py b/zabbix_utils/version.py index 509bc53..7a3edd3 100644 --- a/zabbix_utils/version.py +++ b/zabbix_utils/version.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. -__version__ = "2.0.0" +__version__ = "2.0.1" __min_supported__ = 5.0 __max_supported__ = 7.0