Habilitación de HTTPS en Spring Boot con certificados de Azure Key Vault

En este tutorial se muestra cómo proteger las aplicaciones de Spring Boot (incluidas Azure Spring Apps) con certificados TLS/SSL mediante Azure Key Vault e identidades administradas para recursos de Azure.

Las aplicaciones de Spring Boot del nivel de producción, ya sea en la nube o en un entorno local, requieren cifrado de un extremo a otro para el tráfico de red mediante protocolos TLS estándar. La mayoría de los certificados TLS/SSL que se encuentra se pueden detectar desde una entidad de certificación raíz pública. Sin embargo, a veces no es posible esta detección. Si los certificados no son detectables, la aplicación debe tener alguna manera de cargarlos, presentarlos a las conexiones de red entrantes y aceptarlos de las conexiones de red salientes.

Las aplicaciones de Spring Boot habilitan normalmente TLS mediante la instalación de los certificados. Los certificados se instalan en el almacén de claves local de la máquina virtual Java que ejecuta la aplicación Spring Boot. Con Spring en Azure, los certificados no se instalan localmente. En su lugar, la integración de Spring para Microsoft Azure proporciona una manera segura y sin problemas de habilitar TLS con la ayuda de Azure Key Vault y la identidad administrada para recursos de Azure.

Diagram showing interaction of elements in this tutorial.

Importante

Actualmente, Spring Cloud Azure Certificate Starter versión 4.x o posterior no admite TLS/mTLS, solo configuran automáticamente el cliente de certificados de Key Vault. Por lo tanto, si desea usar TLS/mTLS, no puede migrar a la versión 4.x.

Requisitos previos

  • Una suscripción a Azure: cree una cuenta gratuita.

  • Un kit de desarrollo de Java (JDK) admitido, versión 11.

  • Apache Maven, versión 3.0 o posterior.

  • Azure CLI.

  • cURL o una utilidad HTTP similar para probar la funcionalidad.

  • Una instancia de máquina virtual (VM) de Azure. Si no tiene una, use el comando az vm create y la imagen de Ubuntu proporcionada por UbuntuServer para crear una instancia de máquina virtual con una identidad administrada asignada por el sistema habilitada. Conceda el Contributor rol a la identidad administrada asignada por el sistema y establezca el acceso scope a la suscripción.

  • Una instancia de Azure Key Vault. Si no tiene una, consulte Inicio rápido: Creación de un almacén de claves mediante Azure Portal.

  • Una aplicación de Spring Boot. Si no tiene ninguna, cree un proyecto de Maven con Spring Initializr. Asegúrese de seleccionar Proyecto de Maven y, en Dependencias, agregue la dependencia de Spring Web y, a continuación, seleccione Java versión 8 o posterior.

Importante

Se requiere Spring Boot versión 2.5 o posterior para completar los pasos descritos en este artículo.

Establecimiento de un certificado TLS/SSL autofirmado

Los pasos de este tutorial se aplican a cualquier certificado TLS/SSL (incluso los autofirmados) que esté almacenado directamente en Azure Key Vault. Los certificados autofirmados no son adecuados para su uso en producción, pero son útiles para las aplicaciones de desarrollo y pruebas.

Este tutorial usa un certificado autofirmado. Para establecer el certificado, consulte Inicio rápido: Establecimiento y recuperación de un certificado de Azure Key Vault mediante Azure Portal.

Nota:

Después de establecer el certificado, conceda acceso a la máquina virtual a Key Vault siguiendo las instrucciones de Asignación de una directiva de acceso de Key Vault.

Protección de la conexión a través del certificado TLS/SSL

Ahora tiene una máquina virtual y una instancia de Key Vault y ha concedido acceso a la máquina virtual a Key Vault. En las secciones siguientes se muestra cómo conectarse de forma segura a través de certificados TLS/SSL desde Azure Key Vault en la aplicación Spring Boot. En este tutorial se muestran los dos escenarios siguientes:

  • Ejecución de una aplicación de Spring Boot con conexiones entrantes seguras
  • Ejecución de una aplicación de Spring Boot con conexiones salientes seguras

Sugerencia

En los pasos siguientes, el código se empaquetará en un archivo ejecutable y se cargará en la máquina virtual. No olvide instalar OpenJDK en la máquina virtual.

Ejecución de una aplicación de Spring Boot con conexiones entrantes seguras

Cuando el certificado TLS/SSL para la conexión entrante procede de Azure Key Vault, configure la aplicación siguiendo estos pasos:

  1. Agregue las siguientes dependencias al archivo pom.xml :

    <dependency>
       <groupId>com.azure.spring</groupId>
       <artifactId>azure-spring-boot-starter-keyvault-certificates</artifactId>
       <version>3.14.0</version>
    </dependency>
    
  2. Configure las credenciales de Key Vault en el archivo de configuración application.properties .

    server.ssl.key-alias=<the name of the certificate in Azure Key Vault to use>
    server.ssl.key-store-type=AzureKeyVault
    server.ssl.trust-store-type=AzureKeyVault
    server.port=8443
    azure.keyvault.uri=<the URI of the Azure Key Vault to use>
    

    Estos valores permiten que la aplicación Spring Boot realice la acción load del certificado TLS/SSL, como se mencionó al principio del tutorial. En la tabla siguiente se describen los valores de propiedad.

    Propiedad Descripción
    server.ssl.key-alias El valor del argumento --name que pasó a az keyvault certificate create.
    server.ssl.key-store-type Debe ser AzureKeyVault.
    server.ssl.trust-store-type Debe ser AzureKeyVault.
    server.port El puerto TCP local en el que se van a escuchar las conexiones HTTPS.
    azure.keyvault.uri La propiedad vaultUri del JSON devuelto desde az keyvault create. Ha guardado este valor en una variable de entorno.

    La única propiedad específica de Key Vault es azure.keyvault.uri. La aplicación se ejecuta en una máquina virtual a cuya identidad administrada asignada por el sistema se le ha concedido acceso a Key Vault. Por lo tanto, también se ha concedido acceso a la aplicación.

    Estos cambios permiten a la aplicación de Spring Boot cargar el certificado TLS/SSL. En el paso siguiente, habilitará la aplicación para realizar la acción de aceptación del certificado TLS/SSL, como se mencionó al principio del tutorial.

  3. Edite el archivo de clase de inicio para que tenga el siguiente contenido.

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class SsltestApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SsltestApplication.class, args);
        }
    
        @GetMapping(value = "/ssl-test")
        public String inbound(){
            return "Inbound TLS is working!!";
        }
    
        @GetMapping(value = "/exit")
        public void exit() {
            System.exit(0);
        }
    
    }
    

    Llamar a System.exit(0) desde una llamada REST GET no autenticada se efectúa solo con fines de demostración. No use System.exit(0) en una aplicación real.

    Este código muestra la acción present mencionada al principio de este tutorial. En la lista siguiente se resaltan algunos detalles sobre este código:

    • Ahora hay una anotación @RestController en la clase SsltestApplication generada por Spring Initializr.
    • Hay un método anotado con @GetMapping, con un value para la llamada HTTP que realice.
    • El método inbound simplemente devuelve un saludo cuando un explorador realiza una solicitud HTTPS a la ruta de acceso /ssl-test. El método inbound muestra cómo el servidor presenta el certificado TLS/SSL al explorador.
    • El exit método hace que la JVM salga cuando se invoque. Este método es conveniente para que el ejemplo sea fácil de ejecutar en el contexto de este tutorial.
  4. Ejecute los siguientes comandos para compilar el código y empaquetarlo en un archivo JAR ejecutable.

    mvn clean package
    
  5. Compruebe que el grupo de seguridad de red creado en <your-resource-group-name> permite el tráfico entrante en los puertos 22 y 8443 desde su dirección IP. Para obtener información sobre cómo configurar las reglas del grupo de seguridad de red para permitir el tráfico entrante, consulte la sección Trabajar con reglas de seguridad del artículo Creación, modificación o eliminación de un grupo de seguridad de red.

  6. Coloque el archivo JAR ejecutable en la máquina virtual.

    cd target
    sftp azureuser@<your VM public IP address>
    put *.jar
    

    Ahora que ha compilado la aplicación Spring Boot y la ha cargado en la máquina virtual, siga estos pasos para ejecutarla en la máquina virtual y llamar al punto de conexión de REST con curl.

  7. Use SSH para conectarse a la máquina virtual y, a continuación, ejecute el archivo JAR ejecutable.

    set -o noglob
    ssh azureuser@<your VM public IP address> "java -jar *.jar"
    
  8. Abra un nuevo shell de Bash y ejecute el siguiente comando para comprobar que el servidor presenta el certificado TLS/SSL.

    curl --insecure https://<your VM public IP address>:8443/ssl-test
    
  9. Invoque la ruta de acceso exit para terminar los procesos del servidor y cerrar los sockets de red.

    curl --insecure https://<your VM public IP address>:8443/exit
    

Ahora que ha visto la carga y presenta acciones con un certificado TLS/SSL autofirmado, realice algunos cambios triviales en la aplicación para ver también la acción de aceptación.

Ejecución de una aplicación de Spring Boot con conexiones salientes seguras

En esta sección, modificará el código de la sección anterior para que el certificado TLS/SSL para las conexiones salientes provenga de Azure Key Vault. Por lo tanto, las acciones de load, present y accept se efectúan desde Azure Key Vault.

  1. Agregue la dependencia del cliente HTTP de Apache al archivo pom.xml :

    <dependency>
       <groupId>org.apache.httpcomponents</groupId>
       <artifactId>httpclient</artifactId>
       <version>4.5.13</version>
    </dependency>
    
  2. Incorporación de un nuevo punto de conexión de REST denominado ssl-test-outbound. Este punto de conexión abre un socket de TLS y comprueba que la conexión TLS acepta el certificado TLS/SSL. Reemplace la parte anterior de la clase de inicio por el código siguiente.

    import java.security.KeyStore;
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import com.azure.security.keyvault.jca.KeyVaultLoadStoreParameter;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.ssl.SSLContexts;
    
    @SpringBootApplication
    @RestController
    public class SsltestApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SsltestApplication.class, args);
        }
    
        @GetMapping(value = "/ssl-test")
        public String inbound(){
            return "Inbound TLS is working!!";
        }
    
        @GetMapping(value = "/ssl-test-outbound")
        public String outbound() throws Exception {
            KeyStore azureKeyVaultKeyStore = KeyStore.getInstance("AzureKeyVault");
            KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter(
                System.getProperty("azure.keyvault.uri"));
            azureKeyVaultKeyStore.load(parameter);
            SSLContext sslContext = SSLContexts.custom()
                                               .loadTrustMaterial(azureKeyVaultKeyStore, null)
                                               .build();
    
            HostnameVerifier allowAll = (String hostName, SSLSession session) -> true;
            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, allowAll);
    
            CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();
    
            HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory();
    
            requestFactory.setHttpClient(httpClient);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            String sslTest = "https://localhost:8443/ssl-test";
    
            ResponseEntity<String> response
                = restTemplate.getForEntity(sslTest, String.class);
    
            return "Outbound TLS " +
                (response.getStatusCode() == HttpStatus.OK ? "is" : "is not")  + " Working!!";
        }
    
        @GetMapping(value = "/exit")
        public void exit() {
            System.exit(0);
        }
    
    }
    
  3. Ejecute los siguientes comandos para compilar el código y empaquetarlo en un archivo JAR ejecutable.

    mvn clean package
    
  4. Vuelva a cargar la aplicación con el mismo comando sftp descrito anteriormente en este artículo.

    cd target
    sftp <your VM public IP address>
    put *.jar
    
  5. Ejecute la aplicación en la máquina virtual.

    set -o noglob
    ssh azureuser@<your VM public IP address> "java -jar *.jar"
    
  6. Una vez que el servidor se esté ejecutando, compruebe que el servidor acepta el certificado TLS/SSL. En el mismo shell de Bash en el que emitió el comando curl anterior, ejecute el siguiente comando.

    curl --insecure https://<your VM public IP address>:8443/ssl-test-outbound
    

    Verá el mensaje Outbound TLS is working!!.

  7. Invoque la ruta de acceso exit para terminar los procesos del servidor y cerrar los sockets de red.

    curl --insecure https://<your VM public IP address>:8443/exit
    

Ahora ha observado una ilustración sencilla de las acciones de load, present y accept con un certificado TLS/SSL autofirmado almacenado en Azure Key Vault.

Implementación en Azure Spring Apps

Ahora que tiene la aplicación Spring Boot que se ejecuta localmente, es el momento de moverla a producción. Azure Spring Apps facilita la implementación de aplicaciones de Spring Boot en Azure sin cambios en el código. El servicio administra la infraestructura de las aplicaciones de Spring, con el fin de que los desarrolladores puedan centrarse en el código. Azure Spring Apps proporciona administración del ciclo de vida mediante el uso de una supervisión y un diagnóstico completos, administración de la configuración, detección de servicios, integración de CI/CD e implementaciones azul-verde, entre otros. Para implementar la aplicación en Azure Spring Apps, consulte Implementación de la primera aplicación en Azure Spring Apps.

Pasos siguientes

Para más información acerca de Spring y Azure, vaya al centro de documentación de Azure.