How to fix the Bash bug on CentOS 6

Recently a critical bash bug was discovered.

To fix your CentOS 6 you have to check if you have a vulnerable bash installed. From a non root user, type:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

If you read “vulnerable” as output then you have to update bash. Type su- and then the password to log in as superuser, then type:

yum update bash

Type Y when asked. When the update process is completed, retype the test script:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

And you shouldn’t read the “vulnerable” message anymore.

Read more:

Web fonts and dynamic height calculation issues on jQuery

Recently we’ve nice fonts on web pages like Google Fonts and other web fonts. Take this case, you have to set two divs to the same height. One (div.funny) has some text with Google Fonts, the other is empty.

On Chrome console you type something like:

jQuery(".very", ".myview").height(function () {
  jQuery(this).height(jQuery(this).parent(".myview").find('.funny').height());
});

Div.very and div.funny are now at the same height.

Now if you try to do the same on jquery document ready you got elements with different height. Why?

Because the calculation happens on document ready but before fonts are loaded. The solution is to wrap the code on $(window).load().

$(window).load(function () {
  $(".very", ".myview").height(function () {
    $(this).height($(this).parent(".myview").find('.funny').height());
  });
});

Now .very and .funny are at the same height.

See also:
Calculate Container’s Height After The Font File Loads

Web fonts and dynamic height calculation issues on jQuery

Recently we’ve nice fonts on web pages like Google Fonts and other web fonts. Take this case, you have to set two divs to the same height. One (div.funny) has some text with Google Fonts, the other is empty.

On Chrome console you type something like:

jQuery(".very", ".myview").height(function () {
  jQuery(this).height(jQuery(this).parent(".myview").find('.funny').height());
});

Div.very and div.funny are now at the same height.

Now if you try to do the same on jquery document ready you got elements with different height. Why?

Because the calculation happens on document ready but before fonts are loaded. The solution is to wrap the code on $(window).load().

$(window).load(function () {
  $(".very", ".myview").height(function () {
    $(this).height($(this).parent(".myview").find('.funny').height());
  });
});

Now .very and .funny are at the same height.

See also:
Calculate Container’s Height After The Font File Loads

Untitled document instead of Google Docs list: how to get your document list again

Until yesterday, the shortcut docs.google.com listed the files I’ve on Drive.

Today when I try to access the domain I can only get a blank, Untitled document.

In that case you have to move the cursor on the title and then click on the arrow.

back2gd

You’ll be back on drive.google.com.

How to enable gzip on proxy servers on Apache

I’m starting to use the gunicorn django app using supervisord. Here my configuration:

  • Varnish: port 80
  • Apache: port 8080
  • gunicorn: port 4180 (/path/to/my/manage.py run_gunicorn localhost:4180)

Only the port 80 is exposed to other clients than localhost. The Varnish default backend is Apache (localhost:8080). I have a Drupal installation and a django installation on the same machine: since I want to expose django on the same domain at a defined location, I add to Apache this location:

<IfModule mod_proxy.c>
ProxyRequests Off
ProxyPreserveHost On

<Proxy *>
Order deny,allow
Allow from all
</Proxy>

# on port 4180 gunicorn is running
# @see /etc/supervisor.conf
ProxyPass /foo http://localhost:4180/
ProxyPassReverse /foo http://localhost:4180/

<Location /mypath>
Order allow,deny
Allow from all
AddOutputFilterByType DEFLATE text/html
</Location>
</IfModule>

You can omit AddOutputFilterByType DEFLATE text/html: here I just take the response from gunicorn, compress and then serve to the client in this way:

(client) -> varnish -> apache -> gunicorn

(client) <- varnish <- apache (compress) <- gunicorn
                (X-Varnish-Cache: MISS) 

Here an example of what I get:
Image

It’s a big page, but using gzip from 2.2 MB of the uncompressed page I get 417 KB gzipped text/html, less than 1/4 of the original!

Autolaunch a command and restart it on quit on CentOS

I’ve a django-admin command running as a server thanks to gevent. I want this server to run on boot and autorestart on quit.  StackOverflow give me a hint: use Supervisor.

On a Centos 5 distro:

# find supervisor for your distro...
yum search supervisor
# ...and install it
yum install supervisor.noarch
nano /etc/supervisord.conf

At the end of the file, add a new program:

[program:myfunnydjangocommand]
command=/usr/bin/env python26 /usr/local/etc/django-apps/foo/manage.py tcpapi 4114
priority=999                ; the relative start priority (default 999)
autostart=true              ; start at supervisord start (default: true)
autorestart=true            ; retstart at unexpected quit (default: true)
; startsecs=-1                ; number of secs prog must stay running (def. 10)
; startretries=3              ; max # of serial start failures (default 3)
exitcodes=0,2               ; 'expected' exit codes for process (default 0,2)
stopsignal=QUIT             ; signal used to kill process (default TERM)
; stopwaitsecs=10             ; max num secs to wait before SIGKILL (default 10)
; user=root                   ; setuid to this UNIX account to run the program
log_stdout=true             ; if true, log program stdout (default true)
log_stderr=true             ; if true, log program stderr (def false)
logfile=/var/log/myfunnydjangocommand.log    ; child log path, use NONE for none; default AUTO
logfile_maxbytes=1MB        ; max # logfile bytes b4 rotation (default 50MB)
logfile_backups=10          ; # of logfile backups (default 10)

Then, start supervisord.

service supervisord start

Take a look to supervisord log file:

less +G /var/log/supervisor/supervisord.log

You’ll see something like this:

2013-06-07 11:54:16,559 CRIT Supervisor running as root (no user in config file)
2013-06-07 11:54:16,576 INFO /var/tmp/supervisor.sock:Medusa (V1.1.1.1) started at Fri Jun  7 11:54:16 2013
        Hostname: <unix domain socket>
        Port:/var/tmp/supervisor.sock
2013-06-07 11:54:16,645 CRIT Running without any HTTP authentication checking
2013-06-07 11:54:16,654 INFO daemonizing the process
2013-06-07 11:54:16,657 INFO supervisord started with pid 19316
2013-06-07 11:54:16,666 INFO spawned: 'myfunnydjangocommand' with pid 19318
2013-06-07 11:54:17,670 INFO success: myfunnydjangocommand entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

Read documentation about the configuration options but keep in mind your Supervisor version. I don’t use supervisorctl because of this bug, if you get an error simply go with service supervisord… but if you have a newer version this should be already fixed.

Note: myfunnydjangocommand.log doesn’t contain anything useful in my experience but maybe it’s related how I write the output since I’ve written it to use interactively, outputting lines directly to the user. I’ll update this post if I find how to solve this issue.

Django and Drupal integration using drush via SSH

Some months ago I talked about how to achieve a unified login from Django to Drupal using drush. The basic assumption was that both Drupal and Django are on the same server. What if the two components are on different servers?

Paramiko is a SSH2 protocol library aimed to provide simple classes to make SSH connection. Let’s see how the code to call drush on command line changes.

Prerequisites:

  • paramiko
  • on your app settings.py add:
  • DRUPAL_SERVER_SSH_HOST     = '0.0.0.0' # Your host here
    DRUPAL_SERVER_SSH_USERNAME = 'YourRemoteServerUserHere'
    DRUPAL_SERVER_SSH_PASSWORD = 'YourRemoteServerPasswordHere'

    And then:

    assert request.user.drupal_id > 0
    # user id to log in
    drupal_id = str(request.user.drupal_id)
    output = ""
    try:
     # a list with command as first element and arguments following
     get_password_recovery_url = ["drush", "-r", settings.DRUPAL_SITE_PATH, "-l", settings.DRUPAL_SITE_NAME, "user-login", drupal_id]
     # via ssh http://stackoverflow.com/a/3586168/892951
     ssh = paramiko.SSHClient()
     # add to known_host the remote server key if it's not already stored
     # @see http://jessenoller.com/blog/2009/02/05/ssh-programming-with-paramiko-completely-different
     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
     ssh.connect(settings.DRUPAL_SERVER_SSH_HOST, username=settings.DRUPAL_SERVER_SSH_USERNAME, password=settings.DRUPAL_SERVER_SSH_PASSWORD)
     ssh_stdin, output, ssh_stderr = ssh.exec_command(" ".join(get_password_recovery_url))
     output_lines = output.read().splitlines()
     # taking only the first line of the output:
     # e.g. 'http://example.com.it/user/reset/16/1369986816/67k7ReHi97FdtRfdrrXGqqesyz6FXyy7T8jqHiXxsrY/login'
    except:
     # @todo additional statements here
     pass
    finally:
     if ssh:
      ssh.close()
    
    if output_lines:
    drupal_login_url = output_lines[0].replace("http://example.com/", "http://%s/" % settings.DRUPAL_SITE_URL).strip()
    
    destination = "%s?destination=%s" % (drupal_login_url, settings.DRUPAL_LOGIN_DESTINATION)
     return redirect(destination)
    else:
     return HttpResponse('<h1>Wrong request</h1>')
    

    This is the same code of the previous howto, with the difference that drush now is running on a different server of django. You can use the same method to do anything you have to with drush, any time you call this piece of code an SSH connection is opened.

    See also:

Follow

Get every new post delivered to your Inbox.

Join 33 other followers

%d bloggers like this: