Gitlab: configurar el envío de correos electrónicos

Gilab es un software para gestionar repositorios de software. Existe github y gitlab tiene también su repositorio público. Gitlab permite la instalación del software en tu propia infraestructura.

En éste otro post explicaba como instalarlo y actualizarlo con sus repositorios. Hoy veremos como configurar gitlab para que mande correos electrónicos.

Lo primero que tendremos que hacer es modificar el fichero de configuración que se encuentra en /etc/gitlab/gitlab.rb, buscar las siguientes líneas, descomentarlas y configurarlas de la siguiente forma (para que funcione con la configuración del servidor de correo electrónico instalado con modoboa)

# vi /etc/gitlab/gitlab.rb
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "mail.capa8.net"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "noreply@capa8.net"
gitlab_rails['smtp_password'] = "xxxxxxxxxxx"
gitlab_rails['smtp_domain'] = "capa8.net"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_pool'] = true

gitlab_rails['gitlab_email_enabled'] = true

gitlab_rails['gitlab_email_from'] = 'noreply@capa8.net'
gitlab_rails['gitlab_email_display_name'] = 'GitLab Capa8'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@capa8.net'

A continuación ejecutamos lo siguiente para aplicar la nueva configuración

# gitlab-ctl reconfigure

Una vez realizado el reconfigure, entramos en la consola de gitlab y comprobamos que la configuración se haya aplicado y mandamos un correo electrónico de prueba

# gitlab-rails console -e production
--------------------------------------------------------------------------------
 Ruby:         ruby 3.0.6p216 (2023-03-30 revision 23a532679b) [x86_64-linux]
 GitLab:       15.11.0 (96ee37680b6) FOSS
 GitLab Shell: 14.18.0
 PostgreSQL:   13.8
------------------------------------------------------------[ booted in 32.38s ]
Loading production environment (Rails 6.1.7.2)

irb(main):001:Notify.test_email('info@capa8.net', 'Hello World', 'This is a test message').deliver_now

Y si todo es correcto, deberías recibir un correo electrónico de prueba.…

Instalar imapsync en debian 11

Seguimos con temas de correo. Una de las herramientas que uso para migrar buzones de un servidor a otro es imapsync. Vamos a verlo como lo instalamos.

He usado este manual de clouding.io. Al ejecutar el proceso he descubierto una cosa que me ha encantado! normalmente para concatenar comandos de terminal uso &&, he visto que también es posible usar ;!!! me ha gustado esta! :D

Vamos a ver como lo instalamos

Dependencias

root@imapsync:~# apt update && apt install -y libtest-simple-perl libtest-requires-perl libtest-mock-guard-perl libtest-fatal-perl libpar-packer-perl libnet-ssleay-perl libio-compress-perl libdigest-hmac-perl libcrypt-ssleay-perl libssl-dev libauthen-ntlm-perl libclass-load-perl libcgi-pm-perl libcrypt-openssl-rsa-perl libdata-uniqid-perl libencode-imaputf7-perl libfile-copy-recursive-perl libfile-tail-perl libio-socket-inet6-perl libio-socket-ssl-perl libio-tee-perl libhtml-parser-perl libjson-webtoken-perl libmail-imapclient-perl libparse-recdescent-perl libmodule-scandeps-perl libreadonly-perl libregexp-common-perl libsys-meminfo-perl libterm-readkey-perl libtest-mockobject-perl libtest-pod-perl libunicode-string-perl liburi-perl libwww-perl libtest-nowarnings-perl libtest-deep-perl libtest-warn-perl make cpanminus git rcs gcc libproc-processtable-perl

Otra dependencia que me he encontrado que ha pedido instalar (donde he descubierto lo del ;)

root@imapsync:~# apt-get install apt-file; apt-file update

Configurar módulos

Ahora vamos a configurar algunos módulos que serán necesarios para hacer funcionar imapsync

root@imapsync:~# cpanm Crypt::OpenSSL::RSA Crypt::OpenSSL::Random --force
root@imapsync:~# cpanm Mail::IMAPClient JSON::WebToken Test::MockObject Dist::CheckConflicts
root@imapsync:~# cpanm Unicode::String Data::Uniqid --force
root@imapsync:~# cpanm Mail::IMAPClient IO::Socket::SSL

Instalar imapsync

Ahora para instalarlo, vamos a clonar el repositorio e instalaremos el programa

root@imapsync:~# cd /opt && git clone https://github.com/imapsync/imapsync.git
root@imapsync:~# cd imapsync && make install

Si todo está correcto, copiamos el binario en el directorio de binarios

root@imapsync:~# cp imapsync /usr/bin/imapsync

Para comprobar que el programa se ha instalado correctamente usamos:

root@imapsync:~# imapsync --testslive

Usar imapsync

Cuando migramos un servidor de correo, tenemos el servidor antiguo …

Roundcube: cambiar color skin elastic

Hoy he instalado un roundcube y para hacérmelo más fácil he seguido éste post que hice hace un par de añitos (Gracias Blackhold del pasado por este post!).

Con la versión instalada hoy no me funcionaba el tema larry, así que me he quedado con el tema que viene por defecto, elastic. Al poner el logo, el azul de larry quedaba horrorosamente feo, así que he avanzado un poquito más con la personalización de roundcube. Buscando he llegado a este hilo del foro de roundcube. Decían de modificar el fichero styles.less y compilar los css (¿mande?)

Os lo cuento:

Para cambiar el color del botón de login he tenido que modificar el fichero skins/elastic/styles/styles.less y añadir lo siguiente

#login-form button.mainaction {
  background-color: #8a77b0;
  border-color: #123456;

  &:hover {
    background-color: #123456;
  }
}

Para cambiar el color global, por defecto azul claro, el otro fichero que es necesario modificar es skins/elastic/styles/colors.less y definir en @color-main el color que nos interese, en mi caso:

@color-main:                #8a77b0;

A continuación, para generar el css es necesario instalar el paquete “node-less”:

# apt -y install node-less

Y finalmente ejecutar lo siguiente para aplicar los cambios en los ficheros .less

lessc styles.less styles.min.css

Y ahora ya tienes el roundcube con el color que se adecue a tu empresa o la de tu cliente :)…

Nextcloud: definir quota de usuario por CLI con occ

En uno de los nextcloud que administro, tras unos meses de uso el usuario evidentemente quiere mas espacio. En su momento les definí 1Tb cuando la capacidad total del disco es de 2Tb. Ni idea de porqué lo hice así, hoy me he encontrado de nuevo el porqué, y es que no ha habido forma humana para definir en el interfaz web una quota de usuario de 1,5Tb, ni poniendo 1500Gb, ni con espacio, ni sin ni la madre que lo parió. Así que otra forma de hacerlo es mediante el uso del comando occ.

La forma de definir una quota al usuario entonces sería:

root@***cloud:~# su - www-data -s /bin/bash
www-data@***cloud:~$ cd html/nextcloud
www-data@***cloud:~/html/nextcloud$ chmod +x occ
www-data@***cloud:~/html/nextcloud$ ./occ user:setting admin files quota 1500GB

Para ver si se ha aplicado la quota correctamente

www-data@***cloud:~/html/nextcloud$ ./occ user:info admin
  - user_id: admin
  - display_name: admin
  - email: 
  - cloud_id: admin@cloud.***.***
  - enabled: true
  - groups:
    - admin
  - quota: 1500GB
  - storage:
    - free: 589345344031
    - used: 1021267391969
    - total: 1610612736000
    - relative: 63.41
    - quota: 1610612736000
  - last_seen: 2022-09-15T08:59:09+00:00
  - user_directory: /var/www/nextclouddata/admin
  - backend: Database

Fuente: help.nextcloud.com

Django 4 y zonas horarias (timezone)

Aquí estamos con mi primer proyecto en el que tengo que tener en cuenta la zona horaria del usuario que visualiza los datos de la aplicación que estoy creando.

La aplicación recolecta unos datos de unos sensores y debe almacenarlos en la base de datos, pero luego estos datos que almaceno en mi servidor, tienen que corresponder a la hora del país donde se encuentra el sensor. Con esto empiezas a preguntarte, ¿cómo debo almacenar el dato? ¿cómo lo muestro?

Respuesta rápida:
– Pones tu aplicación en UTC
– Almacenas todos los datos de los sensores con UTC
– Visualizas los datos según la configuración horaria del navegador y/o del usuario

Al investigar sobre el tema, me han pasado un post en un blog muy divertido en el que explica todo esto de las zonas horarias, ¿sabías que existen 244 zonas horarias distintas y un total de 195 países? Y no todas las zonas horarias saltan de hora a hora, además de que nos encontramos en un país que tiene el horario de invierno y el de verano, cosa que no tienen todos los países. Vamos, todo esto de las zonas horarias es un cacao guapo, guapo, guapo.

Programando con Django, una cosa que me he encontrado varias veces es que hay 2 librerías que se llaman igual y se usan distinto (import datetime vs from datetime import datetime), un auténtico quebradero de cabeza que hace que código que funciona en una parte, no lo haga en otra. Al …

Consultar procesos con Celery dentro de uwsgi

Después de unos días de trabajo en mi entorno de desarrollo ha llegado el momento de poner la última versión de la aplicación que estoy haciendo a producción. Como el entorno de producción tiene una configuración distinta que la del entorno de desarrollo, aquí han empezado a aparecer los problemas.

Lo primero es que tenemos una cosa ahí enmedio que se llama uwsgi y es quien ejecuta el código, en lugar del python manage.py runserver. Una de las cosas que me he encontrado es que el trozo de código que comprueba que hay tareas ejecutándose en Celery, no funciona correctamente. En mi caso estaba usando

import time, json
_stats = os.popen('celery inspect stats --json').read()
time.sleep(1)
_stats = json.loads(_stats)

No funcionaba correctamente. He tenido que quebrarme un poco la cabeza para inspeccionar que es lo que estaba ocurriendo, por suerte existe una librería que se llama remote_pdb

(venv) root@app-dev:/var/www/html/app# pip3 install remote-pdb

Y añadimos lo siguiente en el trozo de código que queramos inspeccionar que esté corriendo a través de uwsgi

from remote_pdb import RemotePdb
RemotePdb('127.0.0.1', 4444).set_trace()

Y nos connectaremos al trace por telnet

root@digitplan-dev:~# telnet localhost 4444
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
/var/www/html/app/web/rrd.py(465)check_task_status()
-"""
(Pdb) 

A partir de aquí he podido comprobar que algo le pasa a celery, ya que cualquier comando que ejecutase con celery devolvía vacío. No me ha quedado otra que realizar esta comprobación de otra forma

from celery import Celery
_app = Celery("nombreapp")
_running_tasks = _app.control.inspect().active()['celery@' + str(hostname)]
_status 

Django: tareas en background con celery y rabbitmq

Venga, con este post vamos a subir un poco el nivel de nuestras aplicaciones!

Una de las problemáticas que te encuentras al programar con django es que el envío de mails es horrorosamente lento, el proceso de enviar el mail es relativamente rápido, pero no termino de entender el porqué en general es lento (la conexión y sobre todo la desconexión). Para ello una de las soluciones que más o menos ya había aplicado a mi manera con command hacía que la ejecución cada minuto se solapase con la ejecución de minutos anteriores. Hice una ñapa hace unos días hasta encontrar una solución un poco más elegante. Aquí os la traigo! :D

Hay varios posts que hablan de celery, incluso el libro que uso de consulta “Django 3 by Example”, hablan de usar @tank, pero esta opción está descontinuada para la versión 5, así que aquí veremos como hacerlo con Celery 5. Toda la info la he sacado de éste post.

Configurar e instalar celery y rabbitmq en nuestro proyecto/sistema
Lo primero será instalar celery en nuestro proyecto

laura@melatonina:~/dev/whistleblowerbox$ source venv/bin/activate
(venv) laura@melatonina:~/dev/whistleblowerbox$ pip install celery

A continuación instalamos rabbitmq en nuestro sistema. Rabbitmq es el servicio que encolará las tareas que le pasemos y luego las ejecutará en background

root@melatonina:~# apt -y install rabbitmq-server

Ahora en el fichero settings.py de nuestro proyecto añadimos

(venv) laura@melatonina:~/dev/whistleblowerbox$ vi wbox/settings.py
CELERY_BROKER_URL = 'amqp://localhost'

A continuación, en el mismo directorio donde se encuentra el fichero settings.py vamos a crear uno que se …

Django: definir variables de entorno

Cuando estamos desarrollando una aplicación con django es común que tengamos que ir haciendo cambios en el fichero settings.py, también es común que tengamos unas URL y configuración de la base de datos distinta en cada entorno, sea el desarrollo como el de producción.

En esta entrada veremos como definir variables de entorno que podremos cargar de forma independiente depende de la instancia en la que nos encontremos. Esto también nos permitirá de cara al futuro desplegar una aplicación mediante docker, donde definiremos las variables “desde fuera”.

Vamos a empezar pues.

Lo primero será instalar la librería django-environ (cuidado que está environ, esta no nos sirve)

(venv) laura@melatonina:~/dev/proyecto$ pip3 install django-environ

A continuación en el directorio donde está el fichero settings.py de nuestro proyecto añadiremos un fichero con el siguiente contenido:

(venv) laura@melatonina:~/dev/proyecto$ vi proyecto/.env
DJANGO_SECRET_KEY=************************************************
DJANGO_ALLOWED_HOSTS=localhost,proyecto.capa8.net
DJANGO_DEBUG=True
DATABASE_URL=postgres://proyecto_user:********@localhost:5432/proyecto_db
ENV=development

# Database settings
DB_NAME=proyecto_db
DB_USER=proyecto_user
DB_PASSWORD=********
DB_HOST=localhost

Es importante que no haya ningún espacio ni antes ni después del =, ya que sino fallará.

A continuación en el fichero proyecto/settings.py modificamos las siguientes líneas:

(venv) laura@melatonina:~/dev/proyecto$ vi proyecto/settings.py
import environ, os

env = environ.Env()
environ.Env.read_env()

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('DJANGO_SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!
# False if not in os.environ because of casting above
DEBUG = env('DJANGO_DEBUG')

ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", [])

# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
    'default': env.db('DATABASE_URL'),

Proxmox: redireccionar puerto con iptables y preservar la ip

Hace un par de años hice éste post sobre como redireccionar puertos a los contenedores que se encuentran dentro de un proxmox. Esta solución me fué de maravilla hasta que me di cuenta que el servidor nginx que estaba escuchando en los puertos 80 y 443 en lugar de guardar la IP del cliente que se conectaba, aparecía la IP del proxmox, cosa que creaba algunos problemas a la hora de bloquear intentos de acceso, ya que bloqueabas el acceso a todo el mundo… vamos… un pequeño problemilla de nada :P

Hace unos meses me propuse encontrar la solución pero hasta ahora no me ha sido urgente solucionarlo, ya que tengo que configurar otro proxmox con una ip compartida entre varios contenedores, cada uno de ellos con sus servicios y la cosa era mirar como afinaba esto… así que tras unos 4 días de estar rascando, aquí os traigo la solución :)

iptables -A PREROUTING -t nat -i vmbr0 -d 163.172.109.193 -p tcp --dport 443 -j DNAT --to-destination 192.168.1.2:443
iptables -A FORWARD -p tcp -d 192.168.1.2 --dport 443 -j ACCEPT
iptables -A FORWARD -i vmbr1 -j ACCEPT
iptables -A FORWARD -i vmbr0 -o vmbr1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i vmbr1 -o vmbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o vmbr0 -j MASQUERADE

– La primera regla de PREROUTING, los paquetes entran por la interfaz vmbr0 des de la ip 163.172.109.193, que es la ip pública del servidor. Mandamos todo el …

Gestionar wordpress por terminal con wp-cli

Lo bonito de meterte en problemas es que para salir de ellos tienes que rebanarte los sesos para encontrar soluciones. Dentro de estas soluciones suelen aparecer metodologías, programas y funcionalidades nuevas. En el post de hoy, tras el marronaco del otro día, la herramienta que reapareció en mi terminal fue wp-cli (como los libros aquellos que has oído a hablar de ellos, pero un día aparece de nuevo y te lo comes como un sabroso manjar).

Este fin de semana pasado el proyecto de kaosenalred.net se ha reconvertido a lanueve.info. En su momento hice este otro post explicando como crawlear una página web con wordpress con httrack. El cliente, en lugar de esperar unas horas mas y arreglar el problema de su wordpress, decidió montar otro wordpress. En el lapso de tiempo de la web vieja y la nueva, en el nuevo wordpress han creado la friolera de 2600 entradas nuevas. A la hora de fusionar las dos webs había varias formas, la escogida esta vez ha sido usar Herramientas Exportar, lo cual te genera un fichero .xml (WXR) con las categorías, los autores, los medios y las entradas. Mi experiencia en importaciones de este tipo en el pasado ha sido que en blogs con mucho contenido es timeout y problemas asegurados, así que otra solución es wp-cli. wp-cli es un programa para gestionar instancias de wordpress.

Para instalarlo, descargaremos el fichero wp-cli.phar, le daremos permisos de ejecución y lo guardaremos al directorio /usr/local/bin/ como wp:

root@planet3D:~# curl