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)

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

Substituir Apache por nginx en Centos7 + Virtualmin

Somos fanáticos de NGINX, no podemos ocultarlo, es más complicado tunearlo pero los rendimientos son sorprendentes, entre los beneficios que nos da nginx es que no estamos limitados a correr servicios para PHP sino que tambien podemos usar Ruby o cualquier tipo de SOCKET.

Los pasos a seguir son los siguientes:

[bash]
service httpd stop ; chkconfig httpd off
yum install nginx
systemctl start nginx
yum install wbm-virtualmin-nginx wbm-virtualmin-nginx-ssl
[/bash]

 
Login to Virtualmin as root, and go to Webmin -> Servers -> Nginx Webserver and make sure that Nginx and its configuration files are found.
If not, click on the Module Config link and set the config and command paths correctly.

Return to Virtualmin, and go to System Settings -> Features and Plugins.

Un-check the “Apache website” , “SSL website” and “DAV Login”, “Mailman”, “Protected web directories”, “AWstats reporting” and “Subversion repositories” features.

Check the “Nginx website” and “Nginx SSL website” features, then click “Save”.

If Virtualmin reports that any other features cannot be used without Apache, go back and de-select them too.

Go to the System Information page and click Refresh system information in the top right.

Verify that Nginx is shown as running in the “Status” section.

Esto es todo amigos,..

FUENTE: https://www.virtualmin.com/documentation/web/nginx

Instalar un servidor web para alojar páginas webs desde cero

Hola amigos,

Hoy vamos a instalar un servidor web para dar servicio de páginas web y correo electrónico a nuestros clientes.
Lo primero que tenemos que hacer es conseguir un servidor, el mercado tiene un amplio abanico de posibilidades, si quereis algo para hacer unas pruebas, que no sea caro y podáis probar diferentes distribuciones os recomiendo echarle un vistazo a Digital Ocean, son una empresa con años de experiencia, pioneros en la tecnología que implementan y con una infraestructura verdaderamente robusta. Muy pronto van a contar con un Data Center en Gibraltar y la latencia para España va a ser extraordinaria.

La instalación es muy sencilla, los comando son muy pocos los que se necesitan para echar a andar el servidor web, al final de este POST teneis unas notas sobre que hacer después y por donde seguir.
Este post está dedicado a como hacerlo funcionar en un Centos, al día de hoy estamos usando la version 7 a 64bits, pero se puede hacer con Ubuntu el sistema recomendado por este panel de control es la de RedHat o en nuestro caso Centos7.
NOTA: Podeis seguir el ejemplo usando una máquina virtual con programas tales VirtualBox podeis bajaros la distro desde aqui: Centos Mirrors arrancar desde el CD e instalar la version mínima, son solo 600Mb.
Yo en mi caso dispongo de una distribución virtualizada para los desarrollos, luego solo tengo que exportar e importar el servidor virtual en el servidor de producción.

El único requerimiento previo antes de poder instalar el sistema es aseguraros de tener un nombre de HOST válido. Para ello debemos correr el siguiente comando hostname -f y os debe mostrar un nombre de internet válido tipo servidor.dominio.com. Este ha de tener una reversa o de lo contrario los clientes no podrán acceder a tu servidor. Este paso es el más importante. Piensa antes de decir que YES.

Una vez tengamos nuestro Centos7 64bits recien instalado, y hemos comprobado que el Hostname es válido, vamos a poceder a instalar todo nuestros paquetes para convertir este equipo en un autentico servidor web, para ello nos vamos a entrar por ssh al servidor, entra como root en nuestra nueva máquina y vamos a correr los siguientes comandos:

[code]
yum update
# si no teneis wget instalado porque no viene con la instalación mínima de Centos:::
yum install wget
# nos descargamos el instalador de nuestro servidor web
wget http://software.virtualmin.com/gpl/scripts/install.sh
/bin/sh install.sh
[/code]

Esto es todo, pasado unos minutos podemos acceder a nuestro recien creado webserver a través de la dirección https://servidor.dominio.com:10000, solo tendrás que substituir servidor.dominio.com por el hostname que hayas elegido.

Siguientes recomendaciones a seguir:

Cambiar la version de PHP5 a PHP7, podeis seguir este tutorial.
Cambiar la version de MariaDb5 a MariaDb10, tambien tenemos un tutorial a medida.
Cambiar Apache por Nginx, aqui teneis el tutorial (Este paso es para usuario avanzados).
Activar / Instalar FirewallD, la seguridad es muy importante, no menosprecies una buena configuración del firewall.
Instalar Fail2Ban, nos protege de ataques por fuerza bruta.