Qué hacer tras perder acceso SSH en un servidor Ubuntu alojado en Digital Ocean

Es posible (y ocurre a menudo) que olvidemos las credenciales de autenticación de nuestras máquinas virtuales o que incluso, como es mi caso, se nos estropée el único ordenador con el cuál tenemos acceso SSH a nuestro.

Los pasos para recuperarnos de esta situación son los siguientes:

  1. Obtener acceso 'físico' a nuestro servidor
  2. Resetear credenciales SSH en Digital Ocean
  3. Resetear credenciales del servidor
  4. Comprobar permisos SSH

Obtener acceso 'físico' a nuestro servidor

En primer lugar debemos acceder a través de Digital Ocean a nuestro droplet. Para ello:

  1. Acceder a nuestra cuenta de Digital Ocean
  2. Acceder al Droplet cuyo acceso queremos recuperar
  3. Acceder a la sección Access (https://cloud.digitalocean.com/droplets/**dropletId**/access)
  4. Iniciar la consola

Resetear credenciales SSH en Digital Ocean

Abre una nueva ventana en tu navegador y ve a la sección de seguridad dentro de tu cuenta de Digital Ocean: https://cloud.digitalocean.com/account/security

En el apartado SSH keys, asegúrate de que tienes acceso a todos los dispositivos cuyas claves aparecen listadas. Elimina las claves que ya no necesites.

A continuación, añade las claves SSH de la máquina desde la que te conectarás via SSH a tu Droplet.

Resetear credenciales del servidor

Con la consola de Digital Ocean abierta, vamos a buscar el usuario cuyo acceso has perdido. Ten en cuenta que ahora mismo eres root:

  1. Lista los usuarios del sistema con less /etc/passwd
    Esto devolverá una lista como:

    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/usr/sbin/nologin
    man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
    lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
    mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

    La mayoría de las entradas son usuarios del sistema. Busca el nombre del usuario cuyo acceso quieres recuperar por SSH.

  2. Resetea la contraseña del usuario con passwd username

  3. Habilita el acceso con contraseña via SSH al servidor desde tu ordenador local

    • En la consola de Digital Ocean:
      1. nano /etc/ssh/sshd_config
      2. Edita PermitRootLogin no
      3. Edita PasswordAuthentication yes
      4. Guarda cambios
      5. systemctl restart ssh
    • En la consola de tu ordenador local:
      1. ssh-copy-id [email protected]
      2. Prueba a conectarte con ssh [email protected]
    • En la consola de Digital Ocean:
      1. nano /etc/ssh/sshd_config
      2. Edita PasswordAuthentication no
      3. systemctl restart ssh

Comprobar permisos SSH

Antes de terminar, asegúrate de que en tu Droplet:

  1. Configuración del archivo sshd_config
    • PermitRootLogin no (ssh [email protected] debería devolver Permission denied (publickey).)
    • PasswordAuthentication no

Eliminar código de idioma de post existentes Polylang en WordPress

Eliminar el código de idioma de tus páginas wordpress con polylang

Este artículo podría ser útil para aquellos usuarios de WordPress que tengan instalado Polylang y que tras haberlo usado durante un tiempo con el código de idioma incluido en la url de su web, ahora quieren hacer que el idioma se detecte de forma dinámica eliminando por tanto este código de idioma de la url.

Es decir, imagina que tienes un post llamado mi-post. Hasta ahora para acceder a dicho post tus usuarios accedían a través de una url como mi-sitio.com/es/mi-post, y ahora quieres que se pueda acceder a través de mi-sitio.com/mi-post.

Para hacer este cambio sin que repercuta en la experiencia de usuario de tus visitantes, accede via ftp a la raíz de tu instalación WordPress, encuentra el archivo .htaccess, ábrelo, edítalo y añade el siguiente código:

# BEGIN WordPress
RewriteRule ^es/(.+)$ /$1 [R=301,L]
RewriteRule ^en/(.+)$ /$1 [R=301,L]

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

Redirigir todo el tráfico de una web a un nuevo dominio

Existen numerosas soluciones para redirigir todo el tráfico de una web hacia un nuevo dominio conservando el esquema de la url. Es decir, si tienes una web llamada 'sitioA.com' (con enlaces como sitioA.com/contacto, sitioA.com/post=324, etc) y quieres redirigir todas las direcciones a 'sitioB.org' (con enlaces como sitioB.org/contacto, sitioB.org/post=324, etc), lo más sencillo es modificar el archivo .htaccess que se debería encontrar en la raíz de tu sitio. Si no existe, puedes crearlo a través de SSH, gestor de archivos o FTP. Una vez localizado el archivo .htaccess, tan sólo has de abrirlo y pegar las dos siguientes líneas de código, sustituyendo DOMINIO por el nuevo dominio de tu sitio, que ha de incluir el protocolo (http / https). Por ejemplo, podrías sustituir DOMINIO por https://www.sitioB.org/Recuerda incluir la / al final de la nueva dirección.

El código que has de copiar al comienzo de tu archivo .htaccess:

[code]
RewriteEngine on
RewriteRule ^(.*)$ DOMINIO$1 [R=301,L]
[/code]

Esta forma de redirección es totalmente independiente a la tecnología que tu sitio use. Es decir, si tras hacer esto borraras todos los archivos de sitioA.com (incluyendo bases de datos, etc), la redirección seguiría funcionando, siempre y cuando no elimines el archivo .htaccess. Se trata, además, de una redirección tipo 301, lo cual indica a, por ejemplo, el motor de búsqueda de Google, que esta redirección es permanente; que sitioA.com es ya un dominio que debería quedar en desuso.

 

Si tienes dudas o curiosidad por saber cómo funciona el archivo .htaccess, puedes echar un ojo a mi artículo ' Entendiendo el archivo .htaccess' haciendo clic aquí.

 

[give_form id="10162"]

Modificar plantilla de Administración en Django 1.11 + (1.8+)

El siguiente snippet pretende ilustrar de forma directa y sencilla cómo funciona el sistema de plantillas que Django incorpora desde la versión 1.8, aplicado a la sobre-escritura de la plantilla de administración. Para detalles técnicos está la fabula documentación de Django.

Comencemos creando una carpeta de nombre templates, dentro de esta otra llamada admin, y dentro de admin un archivo al que llamaremos base_site.html. La estructura deberá quedar como sigue:

proyecto
*proyecto
*app1
*app2
*appn
*templates
**admin
***base_site.html

A continuación entra en la carpeta de plantillas de administración de Django en Github: https://github.com/django/django/tree/master/django/contrib/admin/templates/admin

Dentro de este directorio busca el archivo base_site.html. Ábrelo, copia el código que haya en su interior y pégalo en el archivo base_site.html que creaste en el paso anterior.

[code language="python"]
{% extends "admin/base.html" %}

{% block title %}Título de la página{% endblock %}

{% block branding %}

&amp;amp;amp;lt;h1 id="site-name"&amp;amp;amp;gt;&amp;amp;amp;lt;a href="{% url 'admin:index' %}"&amp;amp;amp;gt;Cabecera de la página&amp;amp;amp;lt;/a&amp;amp;amp;gt;&amp;amp;amp;lt;/h1&amp;amp;amp;gt;

{% endblock %}

{% block nav-global %}{% endblock %}
[/code]

Te quedará algo así, y aquí podrás modificar lo que tú quieras, pero para que los cambios aun se vean reflejados aun nos queda un paso más.

Entra en el archivo settings.py de tu proyecto y encuentra el bloque de código TEMPLATES. Dentro del diccionario DIRS, introduce la siguiente ruta:

os.path.join(BASE_DIR, 'templates'),

Deberá quedar algo como:

[code language="python"]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},

]
[/code]

Y ahora sí, al recargar tu página de administración podrás ver que los cambios han surtido efecto. A partir de esto, todo es probar a modificar lo que quieras a partir del proyecto Django original en GitHub.

Registro de usuario en Django Rest Framework

Mi solución de registro de usuarios a través de una API rest. No he encontrado ejemplos completos en ninguna parte, y este código bien pudiera no ser óptimo.
[code language="python"]
#serializers.py
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (AllowAny, )

def create(self, request, *args, **kwargs):
"""
http POST 127.0.0.1:8000/users/ By default asks for permission.
:param request:
:return:
"""
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.create(validated_data=request.data)
#serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
[/code]

[code language="python"]
#views.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
"""
Returns all the Avatars id related to one user. Must set the related_name from the many part.
"""
avatars = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
model = User
fields = ('id', 'email', 'avatars', 'account_type', 'password', 'username')

def create(self, validated_data):
"""
We've to override the create serializer method in order to call to our custom create_user
http://www.django-rest-framework.org/api-guide/serializers/#saving-instances
:param validated_data: Incoming POST data
:return: A created user instance
"""
account_type = AccountType.objects.get(pk=validated_data['account_type'])
email = validated_data['email']
password = validated_data['password']
username = validated_data['email']
return User.objects.create_user(username=username, account_type=account_type, email=email, password=password) # Alternative: User.objects.create(**validated_data)
[/code]

Fecha de creación y edición automáticas en Django

En Django la fecha de creación y modificación no se añade por defecto a nuestros modelos, como sí ocurre en otros frameworks como Ruby On Rails. La forma más elegante de añadir esta funcionalidad a tus modelos es crear una clase base que herede de models.Model y luego hacer que todos tus modelos hereden de la misma.

[code language="python"]
class BaseModel(models.Model):
created_date = models.DateTimeField(_('Date created'), auto_now_add=True)
modified_date = models.DateTimeField(_('Date modified'), auto_now=True)

class Meta:
abstract = True
[/code]

El resto de nuestros modelos donde queramos tener las fechas de creación y actualización automáticas deberán ser del tipo:

[code language="python"]
class AccountType(BaseModel):
name = models.CharField(_('Name of this account type ie: Free'), blank=False, max_length=254)
description = models.TextField(_('Explain the user what he can and can not do with this account'), blank=True, max_length=512)
[/code]

Obtener link de un ImageField en Django Rest Framework

ImageField difiere de FileField en cuanto a que el método .link no está disponible. Enviar vía DRF (Django Rest Framework) el link a una imagen no es un proceso "trivial". Mi implementación se basa en usar Serializer Method Field. El código que sigue creo que es bastante esclarecedor:

serializers.py

[code language="python"]
from rest_framework import serializers
from .models import User, Avatar
from django.conf import settings

class AvatarSerializer(serializers.ModelSerializer):
""" Returns all the avatars """
download_link = serializers.SerializerMethodField()
class Meta:
model = Avatar
fields = ('id', 'user', 'avatar', 'download_link')

# GET Must be in front
def get_download_link(self, obj):
return '%s/%s' % (settings.MEDIA_ROOT , obj.avatar.name)
[/code]

models.py

[code language="python"]
class Avatar(models.Model):
""" Related_name must be set in order to catch related user avatars in the serializer"""
user = models.ForeignKey(User, related_name='avatars')
is_active = models.BooleanField(_('Show this avatar'), default=False)
avatar = models.ImageField(_('Your profile picture'), blank=False, upload_to=set_avatar_directory, help_text=_('Let others see a picture or artwork that identifies you.'))

[/code]

Aun así, es posible que exista una forma más óptima de llevar a cabo este proceso. Si la conoces no dudes en escribir un comentario o ponerte en contacto conmigo.

Fecha por defecto en modelo Django 1.11

Existen dos formas de incluir una fecha por defecto en Django. La primera, que había estado empleando en versiones del framework anteriores sería la siguiente:

[code language="python"]
import datetime
date_of_birth = models.DateField(_('Date of Birth'), default=datetime.date.today())
[/code]

Sin embargo la forma óptima de establecer esta fecha por defecto es empleando el módulo de Django timezone:

[code language="python"]
from django.utils import timezone
date_of_birth = models.DateField(_('Date of Birth'), default=timezone.now)
[/code]

El motivo tiene que ver con el sistema de zonas horarias que viene por defecto activado en Django. Si se emplea la primera opción, tu aplicación no operará de forma correcta cuando se trate de fechas en distintas partes del mundo o del año (cambios anuales).

Configuración rápida mysql Django para Ubuntu 16 / 14

Enlazar mysql con Django puede dar más de un quebradero de cabeza si no se sigue un orden secuencial de instalación de paquetes.

En primer lugar, asegúrate de instalar un servidor de mysql en tu servidor o equipo de desarrollo. Instala tambén libmysqlclient-dev, necesario para conectar python con mysql.

[code language="bash"]
sudo apt-get install mysql-server
sudo apt-get install libmysqlclient-dev
[/code]

Ahora instala mediante pip o tu gestor de paquetes python la librería mysqlclient.

[code language="bash"]
pip install mysqlclient
[/code]

A continuación entra en tu proyecto Django y abre el archivo settings.py. En la sección DATABASES, introduce el siguiente fragmento de código:

[code language="python"]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': os.path.join(BASE_DIR, 'db.cnf'),
},
}
}
[/code]

Por último, crea un archivo con nombre db.cnf en el directorio raíz de tu aplicación Django, y copia el siguiente texto, sustituyendo las variables por tus datos de configuración:

[code]
[client]
database = nombre_de_tu_db
user = usuario_mysql
password = contraseña_usuario
default-character-set = utf8
[/code]

Esto equivaldría a:

[code language="python"]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'USER': 'usuario_mysql',
'PASSWORD': 'contraseña_usuario'
'NAME': 'nombre_de_tu_db',
},
}
[/code]

La base de datos que introduzcas debe ser creada de forma manual, y tras iniciar el servidor y ver que todo funciona correctamente recuerda hacer todas las migraciones pertinentes.