async def start(self) -> None:
        """Start the aiohttp server."""
        context: ssl.SSLContext | None
        if self.ssl_certificate:
            try:
                if self.ssl_profile == SSL_INTERMEDIATE:
                    context = ssl_util.server_context_intermediate()
                else:
                    context = ssl_util.server_context_modern()
                await self.hass.async_add_executor_job(
                    context.load_cert_chain, self.ssl_certificate, self.ssl_key
                )
            except OSError as error:
                _LOGGER.error(
                    "Could not read SSL certificate from %s: %s",
                    self.ssl_certificate,
                    error,
                )
                return

            if self.ssl_peer_certificate:
                context.verify_mode = ssl.CERT_REQUIRED
                await self.hass.async_add_executor_job(
                    context.load_verify_locations, self.ssl_peer_certificate
                )

        else:
            context = None

        # Aiohttp freezes apps after start so that no changes can be made.
        # However in Home Assistant components can be discovered after boot.
        # This will now raise a RunTimeError.
        # To work around this we now prevent the router from getting frozen
        # pylint: disable=protected-access
        self.app._router.freeze = lambda: None  # type: ignore[assignment]

        self.runner = web.AppRunner(self.app)
        await self.runner.setup()

        self.site = HomeAssistantTCPSite(
            self.runner, self.server_host, self.server_port, ssl_context=context
        )
        try:
            await self.site.start()
        except OSError as error:
            _LOGGER.error(
                "Failed to create HTTP server at port %d: %s", self.server_port, error
            )

        _LOGGER.info("Now listening on port %d", self.server_port)
Exemple #2
0
 def _create_emergency_ssl_context(self) -> ssl.SSLContext:
     """Create an emergency ssl certificate so we can still startup."""
     context = ssl_util.server_context_modern()
     host: str
     try:
         host = cast(str, URL(get_url(self.hass, prefer_external=True)).host)
     except NoURLAvailableError:
         host = "homeassistant.local"
     key = rsa.generate_private_key(
         public_exponent=65537,
         key_size=2048,
     )
     subject = issuer = x509.Name(
         [
             x509.NameAttribute(
                 NameOID.ORGANIZATION_NAME, "Home Assistant Emergency Certificate"
             ),
             x509.NameAttribute(NameOID.COMMON_NAME, host),
         ]
     )
     cert = (
         x509.CertificateBuilder()
         .subject_name(subject)
         .issuer_name(issuer)
         .public_key(key.public_key())
         .serial_number(x509.random_serial_number())
         .not_valid_before(datetime.datetime.utcnow())
         .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=30))
         .add_extension(
             x509.SubjectAlternativeName([x509.DNSName(host)]),
             critical=False,
         )
         .sign(key, hashes.SHA256())
     )
     with NamedTemporaryFile() as cert_pem, NamedTemporaryFile() as key_pem:
         cert_pem.write(cert.public_bytes(serialization.Encoding.PEM))
         key_pem.write(
             key.private_bytes(
                 serialization.Encoding.PEM,
                 format=serialization.PrivateFormat.TraditionalOpenSSL,
                 encryption_algorithm=serialization.NoEncryption(),
             )
         )
         cert_pem.flush()
         key_pem.flush()
         context.load_cert_chain(cert_pem.name, key_pem.name)
     return context
    async def start(self):
        """Start the aiohttp server."""
        # We misunderstood the startup signal. You're not allowed to change
        # anything during startup. Temp workaround.
        # pylint: disable=protected-access
        self.app._on_startup.freeze()
        await self.app.startup()

        if self.ssl_certificate:
            try:
                if self.ssl_profile == SSL_INTERMEDIATE:
                    context = ssl_util.server_context_intermediate()
                else:
                    context = ssl_util.server_context_modern()
                await self.hass.async_add_executor_job(
                    context.load_cert_chain, self.ssl_certificate,
                    self.ssl_key)
            except OSError as error:
                _LOGGER.error("Could not read SSL certificate from %s: %s",
                              self.ssl_certificate, error)
                return

            if self.ssl_peer_certificate:
                context.verify_mode = ssl.CERT_REQUIRED
                await self.hass.async_add_executor_job(
                    context.load_verify_locations,
                    self.ssl_peer_certificate)

        else:
            context = None

        # Aiohttp freezes apps after start so that no changes can be made.
        # However in Home Assistant components can be discovered after boot.
        # This will now raise a RunTimeError.
        # To work around this we now prevent the router from getting frozen
        self.app._router.freeze = lambda: None

        self.runner = web.AppRunner(self.app)
        await self.runner.setup()
        self.site = web.TCPSite(self.runner, self.server_host,
                                self.server_port, ssl_context=context)
        try:
            await self.site.start()
        except OSError as error:
            _LOGGER.error("Failed to create HTTP server at port %d: %s",
                          self.server_port, error)
    def _create_ssl_context(self) -> ssl.SSLContext | None:
        context: ssl.SSLContext | None = None
        assert self.ssl_certificate is not None
        try:
            if self.ssl_profile == SSL_INTERMEDIATE:
                context = ssl_util.server_context_intermediate()
            else:
                context = ssl_util.server_context_modern()
            context.load_cert_chain(self.ssl_certificate, self.ssl_key)
        except OSError as error:
            if not self.hass.config.safe_mode:
                raise HomeAssistantError(
                    f"Could not use SSL certificate from {self.ssl_certificate}: {error}"
                ) from error
            _LOGGER.error(
                "Could not read SSL certificate from %s: %s",
                self.ssl_certificate,
                error,
            )
            try:
                context = self._create_emergency_ssl_context()
            except OSError as error2:
                _LOGGER.error(
                    "Could not create an emergency self signed ssl certificate: %s",
                    error2,
                )
                context = None
            else:
                _LOGGER.critical(
                    "Home Assistant is running in safe mode with an emergency self signed ssl certificate because the configured SSL certificate was not usable"
                )
                return context

        if self.ssl_peer_certificate:
            if context is None:
                raise HomeAssistantError(
                    "Failed to create ssl context, no fallback available because a peer certificate is required."
                )

            context.verify_mode = ssl.CERT_REQUIRED
            context.load_verify_locations(self.ssl_peer_certificate)

        return context