Configurar el nombre de las relaciones polimórficas de Laravel

Con este sencillo truco podemos ajustar el nombre de las relaciones polimorficas con tablas anidadas que por defecto van a tomar el nombre de la clase del modelo.

Lo primero es crear una tabla de relaciones, en nuestro caso vamos a incorporarla dentro del archivo config/app.php

// config/app.php
...
'post_types' => [
    'point' => 'App\Models\Point',
    'post' => 'App\Models\Post',
    'doc' => 'App\Models\Doc',
    'supplier' => 'App\Models\Supplier',
    'page' => 'App\Models\Page',
],
...

A Continuacion nos vamos a nuestro Service Provider, en nuestro ejemplo vamos a usar el que viene por defecto en la instalación del Laravel, app/Providers/AppServiceProvider.php


... use Illuminate\Database\Eloquent\Relations\Relation; ... public function boot() { ... Relation::morphMap(config('app.post_types')); ... }

Ya hemos terminado!

Laravel & Lumen Polymorphic Relations seeder

Se pueden crear tablas con relaciones polimorficas con Laravel de una form muy sencilla, un ejemplo de implementacion de las mismas.

<?php

use Ramsey\Uuid\Uuid;

$factory->define(App\Models\Post::class, function ($faker) {
    $taggable = [
        App\Models\PostVideo::class,
        App\Models\PostAudio::class,
        App\Models\PostText::class,
        App\Models\PostImage::class,
        App\Models\PostYoutube::class,
        App\Models\PostSoundCloud::class,
    ];
    return [
        'title' => $faker->catchPhrase,
        'body' => $faker->text,
        'uuid' => Uuid::uuid4(),
        'hub_id' => rand(1,50),
        'owner_id' => rand(1,50),
        'votes' => rand(0,200),
        'plays' => rand(0,200),
        'featured' => $faker->boolean,
        'active' => $faker->boolean,
        'public' => $faker->boolean,
        'upload_ip' => $faker->ipv4,
        'post_id'   => 1,
        'post_type' => $faker->randomElement($taggable),
    ];
});


$factory->defineAs(App\Models\Post::class, 'audio', function (Faker\Generator $faker) use ($factory) {
    $follow = $factory->raw(App\Models\Post::class);
    $extras = [
        'post_id'   => $faker->randomElement(App\Models\PostAudio::pluck('id')->toArray()),
        'post_type' => App\Models\PostAudio::class,
    ];

    return array_merge($follow, $extras);
});

$factory->defineAs(App\Models\Post::class, 'image', function (Faker\Generator $faker) use ($factory) {
    $follow = $factory->raw(App\Models\Post::class);
    $extras = [
        'img_thumb' => $faker->imageUrl(300, 300),
        'img_cover' => $faker->imageUrl(1900, 450),
        'post_id'   => $faker->randomElement(App\Models\PostImage::pluck('id')->toArray()),
        'post_type' => App\Models\PostImage::class,
    ];

    return array_merge($follow, $extras);
});

$factory->defineAs(App\Models\Post::class, 'soundcloud', function (Faker\Generator $faker) use ($factory) {
    $cloudFiles = [
        'https://soundcloud.com/mr-mark-scott/long-live-the-chief-freestyle-mark-scott',
        'https://soundcloud.com/gusgus/biggi-veira-dj-set-queretaro-part2',
        'https://soundcloud.com/gusgus/sets/deep-inside-demos',
        'https://soundcloud.com/henry-saiz/sets/henry-saiz-for-days-and-nights',
        'https://soundcloud.com/futureclassic/flume-chet-faker-drop-the-3',
        'https://soundcloud.com/wknd/chet-faker-x-wknd-lover',
        'https://soundcloud.com/nickraymondg/flume-chet-faker-drop-the-game-the-golden-pony-remix-free-download',

    ];
    $follow = $factory->raw(App\Models\Post::class);
    $extras = [
        'url' => $faker->randomElement($cloudFiles),
        'post_id'   => $faker->randomElement(App\Models\PostSoundCloud::pluck('id')->toArray()),
        'post_type' => App\Models\PostSoundCloud::class,
    ];

    return array_merge($follow, $extras);
});

$factory->defineAs(App\Models\Post::class, 'text', function (Faker\Generator $faker) use ($factory) {
    $follow = $factory->raw(App\Models\Post::class);
    $extras = [
        'post_id'   => $faker->randomElement(App\Models\PostText::pluck('id')->toArray()),
        'post_type' => App\Models\PostText::class,
    ];

    return array_merge($follow, $extras);
});

$factory->defineAs(App\Models\Post::class, 'video', function (Faker\Generator $faker) use ($factory) {
    $follow = $factory->raw(App\Models\Post::class);
    $extras = [
        'post_id'   => $faker->randomElement(App\Models\PostVideo::pluck('id')->toArray()),
        'post_type' => App\Models\PostVideo::class,
    ];

    return array_merge($follow, $extras);
});

$factory->defineAs(App\Models\Post::class, 'youtube', function (Faker\Generator $faker) use ($factory) {
    $youFiles = [
        'https://www.youtube.com/watch?v=9tBOtSPrNik',
        'https://www.youtube.com/watch?v=UDVtMYqUAyw',
        'https://www.youtube.com/watch?v=rE7GU7SLguo',
        'https://www.youtube.com/watch?v=XINlEYXA3k0',
        'https://www.youtube.com/watch?v=5ANlWQy7-I0',
    ];
    $follow = $factory->raw(App\Models\Post::class);
    $extras = [
        'url' => $faker->randomElement($youFiles),
        'post_id'   => $faker->randomElement(App\Models\PostYoutube::pluck('id')->toArray()),
        'post_type' => App\Models\PostYoutube::class,
    ];

    return array_merge($follow, $extras);
});

PHP Lumen desarrollo rápido

The main purpose of this package its to generate on the fly several classes inside your projects,
on a clean an easy way will provide by default with full CRUD with Swagger generation on a clever and simple
way.

This are the main functionalities you can find on this package-library.

Example:

We are going to create a full CRUD for the models Role inside a Subfolder
called Admin and route prefix v1 crud using the console

$ php artisan make:krud Roles --folder=Admin --prefix=v1

We could do it even more dynamic adding this to your route file and visiting /krud, because will generate it from an array at once.

// routes/web.php
use Illuminate\Support\Facades\Artisan;

$prefix = 'v1';
$theKruds = [
  'ChatMessages' => 'User',
  'ChatUsers' => 'User',
  'Chats' => 'User',
  'Settings' => 'User',
  'Users' => 'User',
  'PrivateMessages' => 'User',
  'Roles' => 'Admin',
];

Route::get('krud', function () use ($theKruds){
  foreach ($theKruds as $kKey => $kVal){
    echo "doing KRUD for $kKey<br>\n";
    Artisan::call('ksoft:krud', [
      'model' => $kKey, '--folder' => $kVal, '--prefix' => $prefix
    ]);
  }
  echo 'All jobs done done.';
});

What will KRUD produce

Will give you following structure from each given model: replacing ‘Example’ with your model name.

Functionality FilePath
Model Controller /Controllers/Subfolder/ExampleController.php
Controller Contract /Contracts/Subfolder/ExampleRepository.php
Model repository /Repositories/Subfolder/ExampleRepository.php
Update Interaction Contract /Contracts/Subfolder/ExampleUpdate.php
Create Interaction Contract /Contracts/Subfolder/ExampleCreate.php
Update Interaction /Interactions/Subfolder/ExampleUpdate.php
Create Interaction /Interactions/Subfolder/ExampleCreate.php

This package will add a single line for building a full crud api route, and will save it to routes\api.php with the following:

/**
 * Chats Krud Resource Route
 */
lumen_resource($router, '/v1/chats', 'v1.chats', 'User\ChatsController');

Full procedure

The full scope of this start making the models by generating the migrations, spend time on this, so, start by:

  • Creating your Migrations.
  • Make Seeders, its not strictly necesary, but you should…
  • Make your model (Working on automate this, follow this guide: Ethereal)
  • Generate CRUD: php artisan ksoft:krud Model
  • Generate Swagger: php artisan ksoft:swagger

Thats all, you got full working API, with its Swagger v2 Specks,

So, basicaly, instead of creating each of this configuration, this package does it for you, isnt it handy? will create the files for each model, this command will produce this files, extending all its base functionality from other packages thats provides an extra layer of functionality.

and now What?

Well, if yo got reading this far all this above might make sense to you, am i right?

You should concentrate in validation, make custom validations if you need them, if you havent done so when creating the models, othewise just concentrate on giving your app the customized functionality, you can overwrite all functionality thanks to the class this files extends from. Just follow the thanks section to have a deeper understanting of what all this is about, or checkout dependancies on composer.json-

Notes

Generating model directly from the table database its one of the next features, right now you could have it done with couple packages, them both not enabled right now.

\\ Ksoft\Klaravel\Console\Commands\MakeKrud
protected function setupModelName()
{
    $model           = $this->appNamespace.$this->argument('model');
    $this->model     = str_replace('/', '\\', $model);
    $modelParts      = explode('\\', $this->model);
    $this->modelName = array_pop($modelParts);
    if ($this->force || !class_exists($this->model)) {
        // $this->call('code:models', ['--table' => snake_case($this->modelName)]);
        // $this->call('infyom:model', ['model' => str_singular($this->modelName), '--fromTable' => 'yes']);
    }
}

Composer y la version de PHP correcta en systemas Linux.

Cuando hay muchas version de php tenemos que hacer malabares para fijar la que nos hace falta, con programas como Laravel debemos que funcionen también para los usuarios por comandos cuando hay que hacer los deploy.

El problema

Tenemos instalada la ultima versión de php pero composer se queja de que no está detectando la última version de PHP.

No podemos installar paquetes porque nos dice que el php no es compatible con lo que nuestro nuevo paquete necesita como requerimiento mínimo.

Soluciones

Las soluciones son varias, pero lo mejor es no tocar demasiado en las librerías instaladas, y mucho menos en un servidor dedicado con clientes con webs de mas de 10 años, donde actualizar php no es una opción viable.

Creo que una de las soluciones mas sencillas es crear unos alias en el perfil del usuario que tiene los problemas, ya sea en el .profile, .bashrc, .bash_profile. Dependerá de tu distribución y forma de lograrte en tu servidor.

Alias

Tendrás que editar el fichero que carga tu usuario al hacer login, añade las 2 primeras líneas, si quieres puedes añadir las demás también, no van a hacer ningún daño.


alias php="/usr/bin/php71" alias composer="php /bin/composer" // ya es estamos, vamos a darle mas comandos. alias ka="php artisan" alias kdump="composer dump autoload"

Espero os ayude, siempre es bueno tener esto a mano para no andar buscando lejos.

Saludos.

Laravel Envoy for Virtualmin servers

Os dejo un enlace al gist donde mantengo una version de las funciones mas utilizadas al preparar un nuevo servidor que va a correr Laravel.

Para que funcione hay que tener instalado Laravel Envoy, y crear un archivo en la raiz del projecto, desde donde se lanzan los camandos.

ej: envoy run deploy conectar al servidor, descarga el commit mas reciente del repositorio que hayamos preconfigurado en nuestro projecto, en nuestro repositorio publico privado y va a instalar los paquetes de del composer que encuentre nuevo.

Podeis ver el archivo completo aquí:

https://gist.github.com/kikoseijo/205c49fbc66aba03bbb6d3ff456124ef

@servers(['remote' => 'xx.xx.com', 'local' => '127.0.0.1'])

@include('vendor/autoload.php')

@setup

    (new Dotenv\Dotenv(__DIR__, '.env'))->load();

    $now = date('Ymd-His');
    $branch = "origin/master";
    $username = "hes";
    $key_email = "no-reply@sunnyface.com";
    $repo_domain = "xx.xx.com";
    $repo_group = "xxx";
    $repo_name = "xx";
    $project_root = "/home/$username/$repo_name";
    $domain = $username.'.xxxxxxx.com';
    $slack = env('SLACK_ENDPOINT');
    $crons = [
        'Laravel Schedule' => [
            '* * * * *',
            "/usr/bin/php71 $project_root/artisan schedule:run >> /dev/null 2>&1"
        ]
    ];
    $alias = [
        'alias php="/usr/bin/php71"',
        'alias composer="php /bin/composer"',
        'alias ka="php artisan"',
        'alias kdump="composer dump autoload"',
    ];
    $envs = [
        'SLACK_ENDPOINT="https://hooks.slack.com/services/xxxxx/xxxxxx/xxxxxxxx"',
    ];
@endsetup


@task('php', ['on' => 'remote'])
    su -l  {{ $username }}
    php -v
@endtask

@task('deploy', ['on' => 'remote'])
    su -l  {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan down --message="Upgrading system.." --retry=60
    git fetch --all
    git reset --hard {{ $branch }}
    git pull origin master
    composer update --no-dev --prefer-dist
    php artisan up
@endtask

@task('composer', ['on' => 'remote'])
    su -l  {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan down --message="Upgrading system.." --retry=60
    composer install --no-dev --prefer-dist
    php artisan up
@endtask

@task('ssh', ['on' => 'remote', 'confirm' => true])
    su -l {{ $username }}
    [ -d ~/.ssh ] || echo "~/.ssh directory does not exist, lets create"
    [ -d ~/.ssh ] || mkdir ~/.ssh
    echo "Adding {{ $repo_domain }} domain to known_hosts"
    ssh-keyscan {{ $repo_domain }} >> ~/.ssh/known_hosts
    cd ~/.ssh
    echo "Deleting ssh keys..."
    rm -rf id_rsa id_rsa.pub
    ssh-keygen -t rsa -C "{{ $repo_domain }}" -b 4096  -N "" -f id_rsa
    cat  ~/.ssh/id_rsa.pub
@endtask

@story('setup')
    download
    install
@endstory

@task('download', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~
    echo "Cloning {{ "git@" . $repo_domain .":". $repo_group ."/" . $repo_name . ".git" }}"
    git clone {{ "git@" . $repo_domain .":". $repo_group ."/" . $repo_name . ".git" }}
    cd ~/{{ $repo_name }}
    echo "Done download"
@endtask

@task('install', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    composer install --no-dev
    [ -e .env ] || echo ".env does not exist, using default .env.example"
    [ -e .env ] || cp .env.example .env
    [ -e .env ] || echo "Generating new KEY for .env"
    [ -e .env ] || php artisan key:generate
    echo "Linking storage folder"
    php artisan storage:link
@endtask

@task('alias', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~
    @foreach ($alias as $alia)
        LINE='{{$alia}}'
        FILE=.profile
        grep -qF "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
    @endforeach
    alias
    echo "Done."
@endtask

@task('add_env', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    @foreach ($envs as $env)
        LINE='{{$env}}'
        FILE=.env
        grep -qF "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
    @endforeach
    cat .env
    echo "Done."
@endtask

@task('publish', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~
    rm -rf public_html
    ln -s ~/{{ $repo_name }}/public ~/public_html
    echo "App should be now live."
@endtask

@task('cron_add', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    echo "# write out current crontab"
    @foreach ($crons as $cron_name => $cron_job)
        echo "{{$cron_job[1]}}"
        croncmd="{{$cron_job[1]}}"
        cronjob="{{$cron_job[0]}} $croncmd"
        ( crontab -l | grep -v -F "$croncmd" ; echo "$cronjob" ) | crontab -
    @endforeach
    echo "# Done addCrons"
@endtask

@task('cron_remove', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    echo "# remove crontabs"
    @foreach ($crons as $cron_name => $cron_job)
        croncmd="{{$cron_job[1]}}"
        ( crontab -l | grep -v -F "$cron_name" ) | crontab -
        ( crontab -l | grep -v -F "$croncmd" ) | crontab -
    @endforeach
    echo "# Done rmCrons"
@endtask

@task('apachelogswatch', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    tail -f /var/log/virtualmin/{{$domain}}_error_log
@endtask

@task('laralogswatch', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    tail -f storage/logs/laravel.log
@endtask

@task('catenv', ['on' => 'local'])
    cat .env
@endtask

@task('klean', ['on' => 'local'])
    php artisan clear-compiled
    php artisan migrate:refresh
    php artisan db:seed
@endtask

@task('dump', ['on' => 'local'])
    composer dump-autoload
    php artisan config:clear
    php artisan view:clear
    php artisan cache:clear
    php artisan clear-compiled
@endtask

@task('up', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan up
@endtask

@task('down', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan down
@endtask

@story('fm', ['on' => ['remote']])

    migrate_fresh
    migrate_seed
    passport
@endstory
@task('migrate_fresh', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan migrate:refresh --force
@endtask
@task('migrate_seed', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    composer update
    php artisan db:seed --force
@endtask
@task('passport', ['on' => 'remote'])
    su -l  {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan passport:keys --force
    php artisan passport:install
    # php artisan vendor:publish --tag=passport-components
@endtask

@story('migrate')
    warning
    run_migration_seed
@endstory

@task('warning', ['on' => ['local']])
    echo "---------------------------------------------"
    echo "---------------------------------------------"
    echo "--------------- "ATTENTION" -----------------"
    echo "---------------------------------------------"
    echo "---------------------------------------------"
    echo "-------------  Are you sure?  ---------------"
    echo "---------------------------------------------"
    echo "---------------------------------------------"
@endtask

@task('run_migration_seed', ['on' => 'remote', 'confirm' => true])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan migrate --force
    @if ($seed)
        php artisan db:seed --force
    @endif
@endtask

@task('m:r', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan migrate:rollback --force
@endtask

@task('rights', ['on' => 'remote', 'confirm' => true])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    chmod -R 0777 public/upload app/storage
    find . -type d -exec chmod 775 {} \;
    find . -type f -exec chmod 664 {} \;
@endtask

@task('reload_php_server', ['on' => 'remote'])
    /etc/rc.d/init.d/php-fcgi-{{str_replace('.', '-', $domain)}} restart
@endtask

@task('reload_nginx', ['on' => 'remote'])
    systemctl restart nginx
@endtask

@task('optimizeInstallation', ['on' => 'remote'])
    echo 'start optimizeInstallation'
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan clear-compiled
    php artisan optimize
@endtask

@task('backupDatabase', ['on' => 'remote'])
    echo 'start backupDatabase'
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan backup:run
@endtask

Espero os ayude.

Reglas para fail2ban 2017

# /maillog
# /secure

# DOVECOT

(?: pop3-login|imap-login): .*(?:Disconnected|Authentication failure|Aborted login \(auth failed|Aborted login \(tried to use disabled|Disconnected \(auth failed).*rip=(?P<host>\S*),.*
pam.*dovecot.*(?:authentication failure).*rhost=(?:::f{4,6}:)?(?P<host>\S*)

# DOVECOT-(ignoreregex)

imap-login: Disconnected.*\(no auth attempts\).*



# POSTFIX

NOQUEUE: reject: RCPT from \S+\[<HOST>\]: 554 5\.7\.1\s*$
NOQUEUE: reject: RCPT from \S+\[<HOST>\]: 450 4\.7\.1 : Helo command rejected: Host not found; from=<> to=<> proto=ESMTP helo=\s*$
NOQUEUE: reject: VRFY from \S+\[<HOST>\]: 550 5\.1\.1\s*$
improper command pipelining after \S+ from [^[]*\[<HOST>\]:\s*$
NOQUEUE: reject: RCPT from (.*)\[<HOST>\]: 454 4\.7\.1\.*
warning: \S+\[<HOST>\]: SASL ((?i)LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed
RCPT from \S+\[<HOST>\]: 454 4.7.1

# POSTFIX-(ignoreregex)

authentication failed: Connection lost to authentication server$

# SPAM
RCPT from \S+\[<HOST>\]: 550 5.7.1
RCPT from \S+\[<HOST>\]: 450 4.7.1
RCPT from \S+\[<HOST>\]: 554 5.7.1
RCPT from \S+\[<HOST>\]: 535.5.7.0
RCPT from \S+\[<HOST>\]: 535 5.7.0

# MAIL-QUOTA
NOQUEUE: milter-reject: RCPT from \S+\[<HOST>\]: 551 5.7.1

(3 email por minuto)

Las 10 mejores parejas de fuentes en 2016

1. Lato and Merriweather

Lato & Merriweather

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Lato:700|Merriweather:300,300italic,700');

h1,h2,h3,h4,h5,h6 {
  font-family: "Lato", sans-serif;
  font-weight: 700;
}
p,a,li,blockquote {
  font-family: "Merriweather", serif;
  font-weight: 300;
}

2. Playfair Display and Source Sans Pro

Playfair Display & Source Sans Pro

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Playfair+Display:900|Source+Sans+Pro:300,300italic,700');

h1,h2,h3,h4,h5,h6 {
  font-family: "Playfair Display", serif;
  font-weight: 400;
}
p,a,li,blockquote {
  font-family: "Source Sans Pro", sans-serif;
  font-weight: 300;
}

3. Montserrat and Roboto

Montserrat & Roboto

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Montserrat:700|Roboto:400,400italic,700');

h1,h2,h3,h4,h5,h6 {
  font-family: "Montserrat", sans-serif;
  font-weight: 700;
}
p,a,li,blockquote {
  font-family: "Roboto", sans-serif;
  font-weight: 400;
}

4. Alegreya and Open Sans

Alegreya & Open Sans

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Alegreya|Open+Sans:300,300italic,700');

h1,h2,h3,h4,h5,h6 {
  font-family: "Alegreya", serif;
  font-weight: 400;
}
p,a,li,blockquote {
  font-family: "Open Sans", sans-serif;
  font-weight: 300;
}

5. Fira Sans and Lato

Fira Sans & Lato

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Fira+Sans:700|Lato:300,300italic,700');

h1,h2,h3,h4,h5,h6 {
  font-family: "Fira Sans", sans-serif;
  font-weight: 700;
}
p,a,li,blockquote {
  font-family: "Lato", sans-serif;
  font-weight: 400;
}

6. Cardo and Lato

Cardo & Lato

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Cardo|Lato:700,400,400italic');

h1,h2,h3,h4,h5,h6 {
  font-family: "Cardo", serif;
  font-weight: 400;
}
p,a,li,blockquote {
  font-family: "Lato", sans-serif;
  font-weight: 400;
}

7. Oswald and Lora

Oswald & Lora

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Oswald|Lora:400,700,400italic');

h1,h2,h3,h4,h5,h6 {
  font-family: "Oswald", sans-serif;
  font-weight: 400;
}
p,a,li,blockquote {
  font-family: "Lora", serif;
  font-weight: 400;
}

8. Montserrat and Vollkorn

Montserrat & Vollkorn

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Montserrat|Vollkorn:400,400italic,700');

h1,h2,h3,h4,h5,h6 {
  font-family: "Montserrat", sans-serif;
  font-weight: 400;
  text-transform: uppercase;
}
p,a,li,blockquote {
  font-family: "Vollkorn", serif;
  font-weight: 400;
}

9. Lora and Average

Lora & Average

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Lora:700|Average');

h1,h2,h3,h4,h5,h6 {
  font-family: "Lora", serif;
  font-weight: 700;
}
p,a,li,blockquote {
  font-family: "Average", serif;
  font-weight: normal;
}

10. * { font-family: “Lato”; }

Lato & Lato

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

@import url('https://fonts.googleapis.com/css?family=Lato:400,400italic,700,300');

h1,h2,h3,h4,h5,h6 {
  font-family: "Lato", sans-serif;
  font-weight: 300;
}
p,a,li,blockquote {
  font-family: "Lato", sans-serif;
  font-weight: 400;
}

Actualizar Centos7 php5 a php7

En esta entrada os indico como actualizar la versión de PHP en un servidor Centos7. Sabemos que PHP7 cuatriplica la velocidad de procesamiento y reduce en un 30% la memoria que necesita, asi que después de valorarlo en algunos entornos en producción nos vemos obligados a instalarlos en todos los servidores nuevos que vamos creando.
Los comandos son sencillos, asumimos que los hacemos con root o si necesitas un sudoer puedes añadirle los comandos necesarios.

Antes de nada, para poder ejecutar el comando yum-config-manager necesitamos tener instalados la herramienta yum-utils, este grupo de herramientas del gestor de paquetes YUM de Centos que van a hacer la vida más fácil. Vamos a ello:

yum install yum-utils

Vamos a importar los repositorios de REMI y habilitarlos, simplemente tenemos que ejecutar un yum update y nuestros sistema estará de forma muy sencilla corriendo de forma nativa la version 7 de PHP.

wget -q http://rpms.remirepo.net/enterprise/remi-release-7.rpm
wget -q https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -Uvh remi-release-7.rpm epel-release-latest-7.noarch.rpm
# PARA PHP 7.0 EJECUTAR:
yum-config-manager --enable remi-php70
# PARA PHP 7.1 EJECUTAR:
yum-config-manager --enable remi-php71
yum update

Voilà, ahora si, lo verificamos y comprobamos que estamos corriendo la version de php elegida, ahora nuestro web server va a servir los WordPress’s 4 veces mas rápido y nuestro servidor puede dar servicio a muchos más clientes usando los mismos recursos.

[root@XXXX ~]# php -v
PHP 7.0.17 (cli) (built: Mar 14 2017 15:14:30) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies

Os lo recomiendo si no lo habeis hecho todavía por miedo a los famoso upgrade.

Esto es todo amigos, 😜

Limpiar sitios webs infectados por virus

Buscar archivos .php modificados recientemente, en este caso -7 en la última semana.

find . -type f -name ‘*.php’ -mtime -7

Buscar dentro de los archivos php código malicioso:

TIP: The blank space followed by a * means zero or more spaces.

find . -type f -name '*.php' | xargs grep -l "eval *(" --color
find . -type f -name '*.php' | xargs grep -l "base64_decode *(" --color
find . -type f -name '*.php' | xargs grep -l "gzinflate *(" --color
find . -type f -name '*.php' | xargs grep -l "eval *(str_rot13 *(base64_decode *(" --color
find . -type f -name '*.php' | xargs egrep -i "(mail|fsockopen|pfsockopen|stream_socket_client|exec|system|passthru|eval|base64_decode) *\("
find . -type f -name '*.php' | xargs egrep -i "preg_replace *\((['|\"])(.).*\2[a-z]*e[^\1]*\1 *," --color

Checkear que archivos pueden haber cambiado, este truco se hace con dos versiones idénticas de WordPress, la tuya y una descargada, vamos a buscar diferencias en los archivos para ver cuales pueden estar comprometidos:

diff -r wordpress-clean/ wordpress-compromised/ -x wp-content

Tip: asegurase de usar la misma version de WordPress que la que tienes instalada.

Buscar en los directorios con permisos de escritura archivos infectados o que puedan estarlo

search_for_php_in_writable

chmod +x search_for_php_in_writable
find . -type f -name '*.php' > phpfiles.txt
~/search_for_php_in_writable &gt; php_in_writable.txt
find . -type d -perm 0777 &gt; writable_dirs.txt

find_non_php_in_writable

chmod +x find_non_php_in_writable;
~/find_non_php_in_writable;
./find_non_php_in_writable | less
./find_non_php_in_writable &gt; suspicious_files.txt

####Tip. Puedes usar grep -v para excluir de la busqueda resultados que contengan palabras de las búsquedas salvadas anteriormente.

grep -iv 'binary file' suspicious_files.txt | less

find_suspicious_php

chmod +x find_suspicious_php;
~/find_suspicious_php
find . -type d -perm 0770 &gt; writable_dirs.txt

si encuentras archivos maliciosos busca en los logs los accesos a estos archivos para encontrar la ip del hacker que ha hecho la intrusion.

grep -i &quot;ejfyebh\.php&quot; /var/log/apache/access.log | less

TIP: El truco está en encontrar la IP, una vez encuentras la ip, haces búsquedas en tus logs de la ip del hacker que te ha atacado y con ellos puedes llegar a encontrar el truco que ha usado para hackear tu sitio web.

Esto es todo amigos, si necesitáis algún tipo de ayuda no dejéis de escribir unos comentarios.

Tunear PHP.ini para Centos7 y Nginx

Cada vez que creamos un nuevo Servidor Virtual nuestro sistema de creación replica el archivo que encuentra en /etc/php.ini, por eso lo que hacemos es editarla al crear el servidor y asi usaremos los mismos parámetros bases para todos los servidores virtuales que vamos añadiendo a cada Servidor Dedicado.

La seguridad es lo primero

Esconder la version de PHP en las cabeceras de las páginas servidas a nuestros usuarios maliciosos:

expose_php = Off

Output buffering – Error 502 Nginx

Deshabilitar output buffering, con esto deshabitamos el ob_flush(), esto evita errores tipo 502 en los servidores Nginx, sinceramente, es cuestión de gustos, si no hay buffer la experiencia del usuario se ve truncada a que termine de ejecutar un script hasta que muestra el resultado en pantalla, el caso más claro es cuando WordPress instala/actualiza los plugins y vamos viendo como se instala uno detrás de otro, con el buffering a Off solo veríamos el resultado final, el servidor retiene todo el contenido hasta que ha terminado todos los proceso que le require la carga de está página de WordPress, lo ideal es subir el limite por defecto que nos pone por defecto de 4096 bytes, pero si necesitamos recursos extras lo podemos subir a 16384 bytes.

output_buffering = Off
– or –
output_buffering = 16384

Tiempo de ejecución de los Script

En nuestro caso, corremos bases de datos bastante grandes, hacemos backups y sincronización de datos, a veces los 30 segundos que nos da PHP por defecto nos viene corta, por eso la subimos a 120 segundos el tiempo de espera a que se ejecute un script en una página php.

max_execution_time = 120

Otros valores

Queremos errores, nada más.

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_NOTICE

Mejor un error que una página en blanco que no dice nada.

display_errors = On

Estos valores dependen de vuestros usuarios, que tipo de aplicaciones utilizan.


post_max_size = 20M
upload_max_filesize = 19M