| Server IP : 104.21.21.239 / Your IP : 216.73.216.11 Web Server : Apache/2.4.68 (Amazon Linux) OpenSSL/3.5.5 System : Linux ip-172-31-69-123.ec2.internal 6.1.176-223.369.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Jul 24 13:34:27 UTC 2026 x86_64 User : ec2-user ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /lib/python3.9/site-packages/cfnbootstrap/ |
Upload File : |
#==============================================================================
# Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# 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.
#==============================================================================
from cfnbootstrap import update_hooks
import cfnbootstrap
import configparser
import logging
import os
import time
import random
try:
import servicemanager
import win32event
import win32service
import win32serviceutil
except ImportError:
logging.warn("Win32 cfn-hup service requires pywin32")
class HupService(win32serviceutil.ServiceFramework):
_svc_name_ = 'cfn-hup'
_svc_display_name_="CloudFormation cfn-hup"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, ''))
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
try:
main_config, processor, cmd_processor = update_hooks.parse_config(os.path.expandvars('${SystemDrive}\cfn'))
except ValueError as e:
servicemanager.LogMsg(servicemanager.EVENTLOG_ERROR_TYPE,
servicemanager.PYS_SERVICE_STOPPING,
(self._svc_name_, ': %s' % str(e)))
return
verbose = main_config.has_option('main', 'verbose') and main_config.getboolean('main', 'verbose')
cfnbootstrap.configureLogging("DEBUG" if verbose else "INFO", filename='cfn-hup.log')
log = logging.getLogger("cfn.hup")
# Install or rotate override CA(s) into the Windows trust store BEFORE any
# HTTPS is attempted by the hup main loop. Safe + cheap on every call:
# idempotent fast-path when the marker matches the current PEM. Any hard
# failure raises CaOverrideError which we DO NOT catch -- letting it
# terminate the service so on-call sees the root cause in the traceback.
try:
from cfnbootstrap._ca_install import (
ensure_ca_override_installed,
CaOverrideError,
format_install_failure,
format_install_tolerated,
is_benign_install_failure,
)
ensure_ca_override_installed()
except ImportError:
log.warning(
"[CaOverrideInstall] override CA subsystem unavailable; "
"skipping override CA install")
except CaOverrideError as _e:
if is_benign_install_failure(_e):
# Transient/benign (concurrent_install / budget_exceeded /
# marker_write): a peer cfn process is doing the install, or the
# certs installed but only the bookkeeping marker failed. Trust
# is (or will be) intact -- log and continue starting the service
# rather than crash it over a first-boot install-lock race.
log.warning(format_install_tolerated(_e))
else:
# A real install failure on a host that HAS an override PEM:
# still terminate the service (do NOT run with system-store-only
# trust), but now the root cause is visible in the Windows Event
# Log.
_msg = format_install_failure(_e)
log.error(_msg)
servicemanager.LogMsg(
servicemanager.EVENTLOG_ERROR_TYPE,
servicemanager.PYS_SERVICE_STOPPING,
(self._svc_name_, ': %s' % _msg))
raise
except Exception as _e:
# Safety net matching the 7 bin tools' trailing `except Exception`:
# any error NOT wrapped as CaOverrideError (an unforeseen bug in the
# install path) must not crash the long-running service at startup.
# Warn and continue rather than take the whole daemon down. Known
# failure shapes are already funnelled into CaOverrideError above.
log.warning(
"[CaOverrideInstall] unexpected error during override CA "
"install; skipping: %s", _e)
if main_config.has_option('main', 'interval'):
interval = main_config.getint('main', 'interval')
if interval < 1:
log.error("Invalid interval (must be 1 minute or greater): %s", interval)
interval = 15
else:
interval = 15
interval = interval * 60 + random.randint(-30, 30)
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
last_normal_hup = 0
delay = 1 * 1000
# Periodic override-CA rotation. The one-shot install above runs once at
# service start; a PEM rotated WHILE the long-lived service is running
# would otherwise not be picked up until restart (2.39 loaded the PEM at
# request time). ensure_ca_override_installed() is idempotent (marker
# fast-path) and install_or_rotate() already rotates on pemSha256 change,
# so re-calling it each cycle is cheap. Off-switch: [main] reload_ca_override.
# getboolean() raises on a value it can't interpret as a bool. This read
# sits AFTER the parse_config try/except and BEFORE the loop, so an
# unguarded exception would propagate out of SvcDoRun and the WHOLE
# cfn-hup service would fail to start over one mistyped knob. Two
# distinct exception families must be caught: ValueError (a value
# outside configparser's BOOLEAN_STATES, e.g. "disabled") AND
# configparser.Error -- specifically InterpolationSyntaxError from a bare
# '%' (e.g. "50%"), which is NOT a ValueError subclass. Default to the
# feature ON (safe: periodic rotation stays enabled) and warn. Read the
# raw value for the log (a plain .get() would itself re-trigger the same
# interpolation error on the '%' case).
reload_ca_override = True
if main_config.has_option('main', 'reload_ca_override'):
try:
reload_ca_override = main_config.getboolean('main', 'reload_ca_override')
except (ValueError, configparser.Error):
try:
_raw = main_config.get('main', 'reload_ca_override', raw=True)
except Exception:
_raw = '<unreadable>'
log.warning(
"Invalid [main] reload_ca_override value %r (expected a "
"boolean); defaulting to enabled", _raw)
last_ca_check = time.time()
while True:
try:
if reload_ca_override and time.time() - last_ca_check >= interval:
try:
from cfnbootstrap._ca_install import ensure_ca_override_installed
ensure_ca_override_installed()
except ImportError:
log.warning("ensure_ca_override_installed unavailable; skipping periodic override CA rotation check")
except Exception:
log.exception("Periodic override CA rotation check failed; will retry next cycle")
last_ca_check = time.time()
if processor and time.time() - last_normal_hup > interval:
processor.process()
last_normal_hup = time.time()
if cmd_processor:
if not cmd_processor.is_registered():
cmd_processor.register()
if cmd_processor.creds_expired():
delay = 20 * 1000
log.error("Expired credentials found; skipping process")
else:
delay = 1 * 1000
cmd_processor.process()
except update_hooks.FatalUpdateError:
log.exception("Fatal exception caught; stopping cfn-hup")
break
except Exception:
log.exception("Unhandled exception")
if win32event.WAIT_OBJECT_0 == win32event.WaitForSingleObject(self.hWaitStop, delay):
log.info("Received shutdown event; stopping cfn-hup")
break