A la hora de montar un entorno de integración continua una opción habitual es querer tener las herramientas tras un Apache en vez de exponer los distintos puertos directamente. Sin embargo, dependiendo de las herramientas a montar su configuración no es del todo evidente. En este tutorial veremos cómo tener Jenkins, Nexus y Sonar en un servidor remoto por HTTPS con Apache.
0. Índice de contenidos
- 1. Introducción
- 2. Entorno
- 3. Punto de partida
- 4. Configuración de sonar
- 5. Configuración de nexus
- 6. Configuración de jenkins
- 7. Configuración de apache
- 8. Securización de las aplicaciones
- 9. Conclusiones
- 10. Referencias
1. Introducción
A la hora de montar un entorno de integración continua una opción habitual es querer tener las herramientas tras un Apache en vez de exponer los distintos puertos directamente. Sin embargo, dependiendo de las herramientas a montar su configuración no es del todo evidente. En este tutorial veremos cómo tener Jenkins, Nexus y Sonar en un servidor remoto por HTTPS con Apache.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro Retina (2.93 Ghz Intel Core 2 Duo, 8GB DDR3).
- Sistema Operativo: Mac OS Yosemite 10.10
- Apache 2
- Nexus-2.11.3-01-bundle
- Jenkins 1.621
- Sonar-5.1.1
3. Punto de partida
Partimos de un entorno de integración continua compuesto por las aplicaciones de Jenkins, Nexus y Sonar ya instaladas con las siguientes características:
- Jenkins está corriendo bajo el dominio example.com:8080.
- Nexus está corriendo bajo el dominio example.com:8081.
- Sonar está corriendo bajo el dominio example.com:9000.
- Apache2 está instalado y tenemos creados los certificados ssl. (En este tutorial se muestra como crear certificados)
- Las tres aplicaciones están ejecutándose en la misma máquina.
Dada esta configuración, queremos instalar un Apache que actúe de reverse proxy de forma que yo pueda redirigir las peticiones de mi entorno de integración de forma segura y sin exponer los puertos de actuación.
Nuestro Apache tiene que:
- Redirigir las peticiones de http://example.com/sonar a localhost:9000 pasando por https si la petición no es segura.
- Redirigir las peticiones de http://example.com/nexus a localhost:8081 pasando por https si la petición no es segura.
- Redirigir las peticiones de http://example.com/jenkins a localhost:8080 pasando por https si la petición no es segura.
- Securizar el acceso de proxy por autenticación de apache, para restringir el acceso.
Para lograr esto, primero cambiaremos el context path de las aplicaciones de Sonar, Jenkins y Nexus, y después configuraremos apache para que actúe como proxy.
Una vez comprobado qué tenemos y qué queremos, ¡vamos al lio! 😉
4. Configuración de sonar
Según el punto de partida establecido, ejecutamos sonar a partir de http://example.com:9000.
Para configurar sonar detrás de Apache, necesitamos que la URL de ejecución de sonar sea como: http://example.com:9000/sonar.
Para cambiar el context path de Sonar, nos vamos a su sonar.properties, que en mi caso está en la carpeta conf dentro de la carpeta de instalación de sonar.
En el fichero sonar.properties es dónde cambiamos la propiedad sonar.web.context.
Reiniciamos el servicio de Sonar, y comprobamos la navegación en la ruta:
http://example.com:9000/sonar
Esta es toda la configuración que necesitamos tocar por la parte de Sonar 😀
5. Configuración de nexus
Nexus utiliza el context path de /nexus por defecto. Si no has alterado este parámetro, pasa directamente al punto 5.2.
5.1 Cambio del context path
De forma muy similar a Sonar:
- Vamos a $NEXUS_HOME/conf/nexus.properties.
- Cambiamos la propiedad nexus-webapp-context-path para que se corresponda con nexus-webapp-context-path=/nexus.
5.2 Forzar la URL base
Nexus posee una propiedad que fuerza la URL base de forma que cuando se realiza una petición actúa como si estuviera siendo llamado desde la URL base que nosotros le digamos.
Esto es justo lo que queremos, ya que será apache quien se encarge de redirigir esa petición a Nexus.
Para forzar la URL en Nexus, tenemos que ir a Administracion –> server –> Application Server Settings donde encontraremos la opción Force Base URL.
Nuestra configuración quedaría de la siguiente manera:
Que no se nos olvide reiniciar el servicio! 😀
6. Configuración de jenkins
Lo primero, igual que en el resto de aplicaciones, es hacer que el context path sea el mismo entre Apache y Jenkins. Para configurar el context path debes modificar el fichero de configuración de Jenkins, localizado en el directorio /etc/default/jenkins.
Para ello, en primer lugar declaramos la variable NAME al principio del documento, y que va a ser utilizada como $NAME.
Por último añadimos esta variable a la propiedad PREFIX, y añadimos el prefijo como parámetro en la variable JENKINS_ARGS como se ve en la siguiente imagen.
Una vez configurado, reiniciamos el servicio.
7. Configuración de apache
Ahora mismo tenemos las aplicaciones corriendo bajo las siguientes URLs:
- Sonar: example.com/sonar
- Nexus: example.com/nexus
- Jenkins: example.com/jenkins
Ahora vamos a configurar nuestro Apache para reciba y maneje estas peticiones, redireccionando del puerto 80 al puerto 443 las peticiones que comiencen con el context path de /sonar, /nexus y /jenkins, de forma que el que hace la petición no sepa donde están los servicios que realiza por debajo.
7.1. Habilitar módulos proxy
Para ello necesitamos habilitar los siguientes módulos de apache:
- mod_proxy: Este módulo permite que Apache actúe como proxy.
- mod_proxy_http: Requiere el módulo anterior para funcionar, y nos permite hacer actuar como proxy para peticiones HTTP y HTTPS.
- mod_rewrite: Nos da una serie de reglas basadas un motor de expresiones regulares para mapear URLs.
- mod_ssl: Nos da soporte para SSL para Apache.
- mod_headers: Nos permite modificar, reemplazar o eliminar cabeceras de las peticiones y respuestas HTTP.
Para habilitar los módulos de apache utilizamos los siguientes comandos:
sudo a2enmod proxy sudo a2enmod proxy_http sudo a2enmod rewrite sudo a2enmod ssl sudo a2enmod headers
NOTA: Reiniciamos el servicio para que se activen los nuevos módulos
7.2. Creación del Site
Ahora crearemos el site donde se harán las redirecciones del puerto 80 al puerto 443, y las redirecciones de apache para los servicios que tenemos preparados.
Para ello creamos el fichero example.conf dentro de la carpeta /etc/apache2/sites-available.
example.conf
<VirtualHost *:80> ServerName example.com #Redirect all http traffic to https if it is pointed at /jenkins /nexus /sonar RewriteEngine On RewriteCond %{HTTPS} off RewriteCond %{REQUEST_URI} ^/(jenkins|nexus|sonar)/?.*$ RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] CustomLog /var/log/apache2/example.log combined ErrorLog /var/log/apache2/example-error.log </VirtualHost> <VirtualHost *:443> SSLEngine on SSLCertificateFile /etc/apache2/ssl/example.crt SSLCertificateKeyFile /etc/apache2/ssl/example.key <Proxy *> Order deny,allow Allow from all </Proxy> ProxyRequests Off ProxyPass /sonar http://localhost:9000/sonar ProxyPassReverse /sonar http://localhost:9000/sonar ProxyPass /nexus http://localhost:8081/nexus ProxyPassReverse /nexus http://localhost:8081/nexus ProxyPass /jenkins http://localhost:8080/jenkins nocanon ProxyPassReverse /jenkins http://localhost:8080/jenkins AllowEncodedSlashes NoDecode CustomLog /var/log/apache2/example-https.log combined ErrorLog /var/log/apache2/example-https-error.log </VirtualHost>
Vamos a analizar que hace este fichero:
- Creamos dos host virtuales de Apache, en los cuales uno escucha por el puerto 80 (estandar de HTTP) y otro por el puerto 443 (estandar de HTTPS). El «*» significa que cualquiera puede acceder a esta ip.
- Dentro de la configuración del puerto, vemos que al principio le indicamos el nombre del servidor, con el dominio que hayamos elegido, en este caso example.com
-
Después habilitamos el motor de redirecciones, y establecemos dos condiciones, que son acumulativas y cada una es más restrictiva que la anterior:
- La primera redirecciona todas las peticiones HTTP a HTTPS.
- La segunda especifica que la URI debe tener en el primer fragmento del context path que hayamos elegido como /nexus, /sonar ó /jenkins de forma que solo estas URL serán redireccionadas al puerto 443.
- Añadimos un par de logs, para tener registro de los accesos y los errores.
- Declaramos el segundo virtual host, que recibirá las peticiones redireccionadas por el puerto 443.
- Activamos la configuración de SSL, indicando el certificado y la clave que tenemos creados para el sitio y los colocamos en el directorio /etc/apache2/ssl
- La etiqueta <Proxy> indica sirve para poder limitar el acceso. En nuestro caso, está habilitado para cualquiera que quiera acceder.
- El parámetro ProxyRequests Off sirve para que Apache no actúe como un forward proxy, ya que nosotros solo queremos que este redireccione las peticiones que vengan de clientes externos.
- El caso de Sonar y Nexus es muy parecido: la directiva ProxyPass mapea los servidores remotos en una URL local haciendo que la URL https://example.com/sonar/example se transforme en https://localhost:9000/sonar/example. La directiva ProxyPassReverse es esencial para que cuando te muevas dentro del mismo sitio, Apache redireccione las cabeceras de forma que no salgas pidiendo la URL interna.
- En el caso de la redireccion de Jenkins, y tal como dice la documentación es necesario añadir las propiedades nocanon y AllowEncodedSlashed NoDecode ya que son requeridas para diversas características de Jenkins
NOTA: En este caso redirigimos a localhost debido a que las tres aplicaciones están instaladas en la misma máquina que apache. En caso de que estén en máquinas distintas simplemente hay que sustituir la url por el dominio donde se encuentren estas aplicaciones
Una vez creado el fichero, necesitamos decirle a Apache que utilice la configuración de este sitio, en lugar de la configuración por defecto. Para ello, ejecutamos los comandos:
a2ensite example a2dissite 000-default
De esta manera habilitamos la configuración que acabamos de crear, y deshabilitamos la configuración por defecto de Apache.
¡¡Ya tenemos nuestro Apache actuando como proxy!! 😀
NOTA: Para que esta configuración funcione es necesario que tengas los certificados del sitio, o dará error al hacer la redirección. Si prefieres hacerlo todo por el puerto 80, entonces elimina las líneas de la redirección y copia las directivas del proxy en el virtual host del puerto 80. El documento quedaría de la siguiente forma:
example.conf
<VirtualHost *:80> ServerName example.com <Proxy *> Order deny,allow Allow from all </Proxy> ProxyRequests Off ProxyPass /sonar http://localhost:9000/sonar ProxyPassReverse /sonar http://localhost:9000/sonar ProxyPass /nexus http://localhost:8081/nexus ProxyPassReverse /nexus http://localhost:8081/nexus ProxyPass /jenkins http://localhost:8080/jenkins nocanon ProxyPassReverse /jenkins http://localhost:8080/jenkins AllowEncodedSlashes NoDecode CustomLog /var/log/apache2/example.log combined ErrorLog /var/log/apache2/example-error.log </VirtualHost>
8. Securización de las aplicaciones
Aunque ya tenemos nuestro proxy funcionando, nunca viene mal una capa de seguridad extra a la hora de proteger nuestros entornos de integración. Lo que vamos a hacer es añadir la seguridad que nos aporta apache.
Para ello, habilitamos el módulo auth_digest, que nos permite realizar una autenticación en la que se encriptan los datos antes de ser enviada.
También nos descargamos la herramienta htdigest, para lo que necesitamos instalar apache2-utils.
sudo a2enmod apt-get install apache2-utils
Reiniciamos el servicio y creamos el archivo auth_digest dentro de la carpeta /etc/apache2/digest. Para ello ejecutamos el comando:
htdigest -c /etc/apache2/auth_digest restringido example
Acto seguido nos pedirá una contraseña para el usuario «example». Vamos a analizar que hace cada parte del comando que acabamos de ejecutar:
- La propiedad -c crea el archivo si no existe.
- La ruta /etc/apache2/auth_digest indica donde se creará el archivo.
- A continuación se selecciona el nombre del realm (restringido), que es el grupo al que pertenece al usuario y suele ser también el valor que se le da la propiedad AuthName en las directivas que usan digest.
- Por último va el nombre del usuario, que en nuestro caos se llama example.
Una vez creado nuestro usuario, modificamos el site example.conf añadiendo la siguiente directiva dentro de las etiquetas <virtualhost *:443></virtualhost>.
#Digest auth for all requests <Location "/*"> AuthType Digest AuthName "restringido" AuthUserFile "/etc/apache2/auth_digest" Require valid-user </Location>
Analizando el archivo vemos que:
- La etiqueta <Location> indica restricciones a nivel de URL, de modo que aquí le decimos que sin importar la URL que sea, nos pida autenticación via digest.
- El tipo de autenticación se indica con AuthType, en AuthName se indica generalmente el realm elegido.
- Se indica donde se encuentra el fichero de autenticación con la propiedad AuthUserFile.
- Por último, se dice que solo se permitan los usuarios que estén autenticados.
Nuestro fichero de configuración queda de la siguiente manera:
example.conf
<VirtualHost *:80> ServerName example.com #Redirect all http traffic to https if it is pointed at /jenkins /nexus /sonar RewriteEngine On RewriteCond %{HTTPS} off RewriteCond %{REQUEST_URI} ^/(jenkins|nexus|sonar)/?.*$ RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] CustomLog /var/log/apache2/example.log combined ErrorLog /var/log/apache2/example-error.log </VirtualHost> <VirtualHost *:443> SSLEngine on SSLCertificateFile /etc/apache2/ssl/example.crt SSLCertificateKeyFile /etc/apache2/ssl/example.key <Proxy *> Order deny,allow Allow from all </Proxy> ProxyRequests Off ProxyPass /sonar http://localhost:9000/sonar ProxyPassReverse /sonar http://localhost:9000/sonar ProxyPass /nexus http://localhost:8081/nexus ProxyPassReverse /nexus http://localhost:8081/nexus ProxyPass /jenkins http://localhost:8080/jenkins nocanon ProxyPassReverse /jenkins http://localhost:8080/jenkins AllowEncodedSlashes NoDecode #Digest auth for all https requests <Location "/*"> AuthType Digest AuthName "restringido" AuthUserFile "/etc/apache2/auth_digest" Require valid-user </Location> CustomLog /var/log/apache2/example-https.log combined ErrorLog /var/log/apache2/example-https-error.log </VirtualHost>
Reiniciamos el servicio y comprobamos como al acceder a la URL http://example.com/sonar se realiza una redirección a https://example.com/sonar, pidiendo antes la autenticación de Apache.
9. Conclusiones
Poner las aplicaciones detrás de un reverse proxy, es habitual en los desarrollos debido a la capa de protección que ofrece frente ataques directos, así como ocultar los puertos en los que son ejecutados las aplicaciones.
10. Referencias
-
https://ghost.org/forum/installation/16061-reverse-proxy-and-ssl
-
https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Apache
- https://books.sonatype.com/nexus-book/reference/install-sect-proxy.html
- http://docs.sonarqube.org/display/SONARQUBE52/Running+SonarQube+behind+a+Proxy