@@ -64,6 +64,9 @@ def __init__(self):
6464 self .db_host = os .getenv ("SCM_DB_HOST" , "localhost" )
6565 self .db_port = os .getenv ("SCM_DB_PORT" , 5432 )
6666 self .db_user = os .getenv ("SCM_DB_USER" , "postgres" )
67+ # use default value of None for security reasons (won't be matched)
68+ self .hc_user = os .getenv ("SCM_HC_USER" , None )
69+ self .hc_password = os .getenv ("SCM_HC_PASSWORD" , None )
6770 password_file_path = os .getenv ("SCM_DB_PASSWORD_FILE" , None )
6871 if password_file_path :
6972 with open (os .path .abspath (password_file_path ), "r" ) as fileobj :
@@ -123,6 +126,7 @@ class ViewType(Enum):
123126# do I hate these globals, but I don't see another way with these frameworks
124127app = FastAPI ()
125128security = HTTPBasic (realm = "Compliance monitor" , auto_error = True ) # use False for optional login
129+ optional_security = HTTPBasic (realm = "Compliance monitor" , auto_error = False )
126130settings = Settings ()
127131# see https://passlib.readthedocs.io/en/stable/narr/quickstart.html
128132cryptctx = CryptContext (
@@ -719,6 +723,22 @@ async def post_results(
719723 conn .commit ()
720724
721725
726+ @app .get ("/healthz" )
727+ async def get_healthz (request : Request ):
728+ """return compliance monitor's health status"""
729+ credentials = await optional_security (request )
730+ authorized = credentials and \
731+ credentials .username == settings .hc_user and credentials .password == settings .hc_password
732+
733+ try :
734+ mk_conn (settings = settings )
735+ except Exception as e :
736+ detail = str (e ) if authorized else 'internal server error'
737+ return Response (status_code = 500 , content = detail , media_type = 'text/plain' )
738+
739+ return Response () # empty response with status 200
740+
741+
722742def pick_filter (results , subject , scope ):
723743 """Jinja filter to pick scope results from `results` for given `subject` and `scope`"""
724744 return results .get (subject , {}).get (scope , {})
0 commit comments