diff --git a/README.md b/README.md index ce6e87e..22441f5 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ Log in to the Spring Boot Admin UI at `http://localhost:8080` to interact with t When registering a service in Spring Boot Admin, note that: * **Docker** - If the Spring Boot Admin is running in a container while the managed service is running in the docker-host directly, the `app_url` and `pyctuator_endpoint_url` should use `host.docker.internal` as the url's host so Spring Boot Admin will be able to connect to the monitored service. * **Http Traces** - In order for the "Http Traces" tab to be able to hide requests sent by Spring Boot Admin to the Pyctuator endpoint, `pyctuator_endpoint_url` must be using the same host and port as `app_url`. +* **HTTPS** - If Spring Boot Admin is using HTTPS with self-signed certificate, set the `PYCTUATOR_REGISTRATION_NO_CERT` environment variable so Pyctuator will disable certificate validation when registering (and deregistering). ## Advanced Configuration The following sections are intended for advanced users who want to configure advanced Pyctuator features. diff --git a/examples/Advanced/pyproject.toml b/examples/Advanced/pyproject.toml index 7e13731..c43ec77 100644 --- a/examples/Advanced/pyproject.toml +++ b/examples/Advanced/pyproject.toml @@ -11,7 +11,7 @@ python = "^3.7" psutil = { version = "^5.6" } fastapi = { version = "^0.41.0" } uvicorn = { version = "^0.9.0" } -pyctuator = { version = "^0.13.1" } +pyctuator = { version = "^0.13.2" } sqlalchemy = { version = "^1.3" } PyMySQL = { version = "^0.9.3" } cryptography = { version = "^2.8" } diff --git a/examples/FastAPI/pyproject.toml b/examples/FastAPI/pyproject.toml index 07d8ba3..87291f7 100644 --- a/examples/FastAPI/pyproject.toml +++ b/examples/FastAPI/pyproject.toml @@ -11,7 +11,7 @@ python = "^3.7" psutil = { version = "^5.6" } fastapi = { version = "^0.41.0" } uvicorn = { version = "^0.9.0" } -pyctuator = { version = "^0.13.1" } +pyctuator = { version = "^0.13.2" } [build-system] requires = ["poetry>=0.12"] diff --git a/examples/Flask/pyproject.toml b/examples/Flask/pyproject.toml index 0b62727..e78be6b 100644 --- a/examples/Flask/pyproject.toml +++ b/examples/Flask/pyproject.toml @@ -10,7 +10,7 @@ authors = [ python = "^3.7" psutil = { version = "^5.6" } flask = { version = "^1.1" } -pyctuator = { version = "^0.13.1" } +pyctuator = { version = "^0.13.2" } [build-system] requires = ["poetry>=0.12"] diff --git a/examples/aiohttp/pyproject.toml b/examples/aiohttp/pyproject.toml index e8ed9ed..5ece2cb 100644 --- a/examples/aiohttp/pyproject.toml +++ b/examples/aiohttp/pyproject.toml @@ -10,7 +10,7 @@ authors = [ python = "^3.7" psutil = { version = "^5.6" } aiohttp = { version = "^3.5.4" } -pyctuator = { version = "^0.13.1" } +pyctuator = { version = "^0.13.2" } [build-system] requires = ["poetry>=0.12"] diff --git a/examples/tornado/pyproject.toml b/examples/tornado/pyproject.toml index 667ef34..2a9334b 100644 --- a/examples/tornado/pyproject.toml +++ b/examples/tornado/pyproject.toml @@ -10,7 +10,7 @@ authors = [ python = "^3.7" psutil = { version = "^5.6" } tornado = { version = "^6.0.4" } -pyctuator = { version = "^0.13.1" } +pyctuator = { version = "^0.13.2" } [build-system] requires = ["poetry>=0.12"] diff --git a/pyctuator/impl/spring_boot_admin_registration.py b/pyctuator/impl/spring_boot_admin_registration.py index 44b0c4f..a55505c 100644 --- a/pyctuator/impl/spring_boot_admin_registration.py +++ b/pyctuator/impl/spring_boot_admin_registration.py @@ -1,12 +1,13 @@ import http.client import json import logging +import os +import ssl import threading import urllib.parse from base64 import b64encode from datetime import datetime - -from http.client import HTTPConnection +from http.client import HTTPConnection, HTTPResponse from typing import Optional, Dict from pyctuator.auth import Auth, BasicAuth @@ -35,6 +36,8 @@ def __init__( self.instance_id = None self.should_continue_registration_schedule: bool = False + self.disable_certificate_validation_for_https_registration: bool = \ + os.getenv("PYCTUATOR_REGISTRATION_NO_CERT") is not None def _schedule_next_registration(self, registration_interval_sec: int) -> None: timer = threading.Timer( @@ -66,15 +69,7 @@ def _register_with_admin_server(self) -> None: headers = {"Content-type": "application/json"} self.authenticate(headers) - reg_url_split = urllib.parse.urlsplit(self.registration_url) - conn = http.client.HTTPConnection(reg_url_split.hostname, reg_url_split.port) - conn.request( - "POST", - reg_url_split.path, - body=json.dumps(registration_data), - headers=headers, - ) - response = conn.getresponse() + response = self._http_request(self.registration_url, "POST", headers, json.dumps(registration_data)) if response.status < 200 or response.status >= 300: logging.warning("Failed registering with boot-admin, got %s - %s", response.status, response.read()) @@ -104,14 +99,7 @@ def deregister_from_admin_server(self) -> None: conn: Optional[HTTPConnection] = None try: - reg_url_split = urllib.parse.urlsplit(deregistration_url) - conn = http.client.HTTPConnection(reg_url_split.hostname, reg_url_split.port) - conn.request( - "DELETE", - reg_url_split.path, - headers=headers, - ) - response = conn.getresponse() + response = self._http_request(deregistration_url, "DELETE", headers) if response.status < 200 or response.status >= 300: logging.warning("Failed deregistering from boot-admin, got %s - %s", response.status, response.read()) @@ -139,3 +127,24 @@ def start(self) -> None: def stop(self) -> None: logging.info("Stopping recurring registration") self.should_continue_registration_schedule = False + + def _http_request(self, url: str, method: str, headers: Dict[str, str], body: Optional[str] = None) -> HTTPResponse: + url_parts = urllib.parse.urlsplit(url) + if url_parts.scheme == "http": + conn = http.client.HTTPConnection(url_parts.hostname, url_parts.port) + elif url_parts.scheme == "https": + context = None + if self.disable_certificate_validation_for_https_registration: + context = ssl.SSLContext() + context.verify_mode = ssl.CERT_NONE + conn = http.client.HTTPSConnection(url_parts.hostname, url_parts.port, context=context) + else: + raise ValueError(f"Unknown scheme in {url}") + + conn.request( + method, + url_parts.path, + body=body, + headers=headers, + ) + return conn.getresponse() diff --git a/pyproject.toml b/pyproject.toml index 2489324..dfc783d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyctuator" -version = "0.13.1" +version = "0.13.2" description = "A Python implementation of the Spring Actuator API for popular web frameworks" authors = [ "Michael Yakobi ",