Este blog es parte de una serie de blogs, por lo que puede encontrar la primera página aquí (https://blogs.sap.com/2023/02/02/sap-cpi-ci-cd-from-from-zero-to-hero/). Esta es la agenda que estamos siguiendo:
Los certificados son uno de los mecanismos más potentes que puede utilizar para proteger sus aplicaciones. Uno de los aspectos clave que también contribuye a la seguridad es que los certificados son artefactos limitados en el tiempo que tienen una fecha de vencimiento. Las fechas de caducidad pueden ser diferentes de un certificado a otro (de acuerdo con las normas de la CA, si utiliza un certificado autofirmado, etc.).
Ahora imagine que trabaja con Cloud Integration y necesita comunicarse con muchas partes diferentes: algunos servidores externos que pertenecen a sus socios, algunas aplicaciones en la nube, otras en las instalaciones controladas por usted mismo. Si alguno de los certificados asociados con los servidores que está utilizando ha caducado, lo más probable es que su interfaz se vea afectada y también deje de funcionar.
Por lo tanto, ¿no sería genial si pudiera saber con tiempo por adelantado que un certificado está a punto de caducar y que debería renovarlo o ponerse en contacto con su proveedor para renovarlo?
Esto era imprescindible para nosotros, afortunadamente cuando estaba configurando CALM para nuestras operaciones, hay una sección de monitoreo de salud que le permite validar las fechas de vencimiento de sus certificados, lo que permite asociar notificaciones para informarlo a su equipo. Además de eso, le permitiría configurar diferentes niveles de umbral con diferentes acciones, lo que le permitiría notificar a diferentes equipos a medida que se acerca la fecha límite, por ejemplo.
Eso era exactamente lo que necesitábamos, así que seguí la configuración e hice una prueba de configuración de un certificado a punto de caducar. Al final, CALM no funcionó como esperábamos, por lo que decidimos optar por nuestra canalización personalizada de Jenkins. A continuación puede encontrar la arquitectura que seguimos. A continuación, detallaré todos los pasos que nos influyeron para renunciar a CALM e ir con la canalización personalizada.
Configuré solo mi correo electrónico como el correo electrónico para recibir notificaciones y comencé a recibir correos electrónicos vacíos cada 5 millones. Me di cuenta de que lo más probable era lo que acabo de configurar. Investigué un poco, no pude encontrar la causa de la ruta y abrí un incidente con SAP. La respuesta: un error, que se arreglaría en poco tiempo. Esperé hasta que me dijeron que estaba listo para usar de nuevo.
Ahora no recibo un correo electrónico vacío, sino un correo electrónico que menciona que hay certificados que se acercan a la fecha de vencimiento con un enlace a CALM. No es óptimo, desde mi perspectiva, ya que tuve que abrir el enlace para ver realmente cuáles son los certificados que están a punto de caducar. Sin embargo, pensé que tal vez haya una manera de configurar el cuerpo de este correo electrónico mediante el uso de marcadores de posición, por lo que no fue un impedimento.
El problema fue que estaba recibiendo los mismos correos electrónicos cada 5 minutos (el tiempo que el recopilador de datos CALM se ejecuta para extraer datos de los inquilinos de integración de la nube). Eso fue definitivamente un no-go, informé, el equipo reconoce que esto podría ser un problema, pero no había ningún plan para solucionarlo en un corto tiempo.
Así que decidimos utilizar un enfoque personalizado de canalización de Jenkins, compuesto por 4 pasos que puede encontrar en el primer diagrama de esta página.
Para esta fundición en la nube, ya proporciona una API muy fácil de usar que le devolverá todas las claves de servicio, por lo que solo necesitábamos evaluar las fechas de vencimiento para ellas. Ejemplo del código a continuación:
///
/// Loads a list of all service keys with certificates available on an cloud foundry (CF) runtime and their respective expiration date
///
def loadCFServiceKeyCertificatesExpirationDates(String token,String cfHost)
{
def certs = [:]
try {
def serviceKeysListResponse = httpRequest acceptType: 'APPLICATION_JSON', customHeaders: [[maskValue: false, name: 'Authorization', value: token]],
ignoreSslErrors: true, validResponseCodes: '100:399, 404', timeout: 30,
url: 'https://' + cfHost + '/v2/service_keys';
if (serviceKeysListResponse.status == 404) {
//invalid Flow ID
error("Unable to list SAP CF service keys certificates expiration dates");
} else {
def serviceKeyJson = new JsonSlurper().parseText(serviceKeysListResponse.content )
// aux list is being created to prevent jenkins issues with global objects not serializable
serviceKeyJson.resources.each {
if (it.entity != null){
def serviceKeyName = it.entity.name
def certificateValue = it?.entity?.credentials?.oauth?.certificate
if(certificateValue!=null && !"".equals(certificateValue))
{
String certB64 = certificateValue.replace("-----END CERTIFICATE-----", "").replace("-----BEGIN CERTIFICATE-----", "").replaceAll("[\\n\\t ]", "").trim()
byte[] encodedCert = Base64.getDecoder().decode(certB64);
ByteArrayInputStream inputStream = new ByteArrayInputStream(encodedCert);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)certFactory.generateCertificate(inputStream);
Date certExpirydate = new SimpleDateFormat("yyyy-MM-dd").parse(cert.notAfter.toTimestamp().toString());
def subjectDN = cert.getSubjectDN().toString()
certs[serviceKeyName] = [certExpirydate:certExpirydate,subjectDN:subjectDN]
}
}
}
}
} catch (Exception e) {
error("Retrieving CF service keys:\n${e}")
}
return certs;
}
Luego, dado que ya teníamos una tubería que descargaba todos nuestros certificados y realizaba copias de seguridad, fue muy fácil usarlo para extraer las fechas de vencimiento de la lista de certificados almacenados en la integración en la nube.
Ejemplo del código a continuación:
///
/// Loads a list of all certificates available on an CPI environment
///
def loadAllNonSAPCertificates(String token,String cpiHost)
{
def certs = [:]
try {
def certificateListResponse = httpRequest acceptType: 'APPLICATION_JSON', customHeaders: [[maskValue: false, name: 'Authorization', value: token]],
ignoreSslErrors: true, validResponseCodes: '100:399, 404', timeout: 30,
url: 'https://' + cpiHost + '/api/v1/KeystoreEntries';
if (certificateListResponse.status == 404) {
//invalid Flow ID
error("Unable to list SAP CPI Certificates");
} else {
def list = new JsonSlurper().parseText(certificateListResponse.content )
// aux list is being created to prevent jenkins issues with global objects not serializable
list.d.results.each {
if (it.Owner != "SAP"){
certs[it.Alias] = [certExpirydate:it.ValidNotAfter,subjectDN:it.SubjectDN] ;
}
}
}
} catch (Exception e) {
error("Retrieving CPI Certificates:\n${e}")
}
return certs;
}
Configuramos SSL para nuestros servidores Jenkins y Gitea que se ejecutan en nuestro servidor local. Este certificado también vence y también debe renovarse, por lo que también podríamos verificar ese vencimiento.
Ejemplo del código a continuación:
def days = 45
try{
println("Checking if gitea/jenkins certificate is expired on CI/CD server")
def thresholdAlertSeconds = 24*3600*days
def result = bat "openssl x509 -checkend " + thresholdAlertSeconds + " -noout -in \"C:/WorkSpaces/Certificates/certificate.pem\""
}catch(Exception exc){
isToSendEmail = true
notifBody += '<tr><td>Jenkins/Gitea certificate</td><td> </td><td>Expires in less than '+days+' days</td></tr>'
}
El paso final es enviar el html final que contiene todos los certificados que están vencidos o a punto de vencer a sus equipos de Integración y Base. Ejemplo de ese código a continuación:
if(isToSendEmail){
notifBody += '</table>'
unstable('Certificates expired or about to expire found!')
emailext mimeType: 'text/html', body: '${JELLY_SCRIPT,template="html"}' + notifBody,
from: "YOUR_CPI_TEAM@YOURDOMAIN.com",
to: 'YOUR_CPI_TEAM@YOURDOMAIN.com;YOUR_BASIS_TEAM@YOURDOMAIN.com',
subject: 'Build unstable in Jenkins for Environment ['+environment+']: $PROJECT_NAME - #$BUILD_NUMBER'
}
Como resultado de ejemplo, puede ver que identificamos el certificado, así como el CN y la fecha de vencimiento y lo informamos en una tabla
Dejamos de hacer un seguimiento de CALM para la caducidad de los certificados, pero la última vez que lo comprobé, ahora el correo electrónico se ve mucho mejor, como puede ver a continuación.
En este tema, introduje las comprobaciones que estamos haciendo con respecto a los certificados que están a punto de caducar. Si está comenzando ahora con este tema y tiene la licencia CALM, le aconsejo que pruebe eso primero y vea si ya se ajusta a sus necesidades. De lo contrario, siempre puede confiar en una canalización similar a la que estamos usando.
También te invito a compartir algunos comentarios o pensamientos en las secciones de comentarios. Estoy seguro de que todavía hay mejoras o ideas para nuevas reglas que beneficiarían a toda la comunidad. Siempre puede obtener más información sobre la integración de la nube en el página de tema para el producto
Calle Eloy Gonzalo, 27
Madrid, Madrid.
Código Postal 28010
Paseo de la Reforma 26
Colonia Juárez, Cuauhtémoc
Ciudad de México 06600
Real Cariari
Autopista General Cañas,
San José, SJ 40104
Av. Jorge Basadre 349
San Isidro
Lima, LIM 15073