Upgrading to PHP 7.3 on Ubuntu Bionic 18.04 LTS

Ubuntu 18.04 ships with PHP 7.2 by default but there are various reasons why you may wish to upgrade to newer versions. For example, active support for it ends later this year – far sooner than the 2023 support window for the OS.

In addition, applications will be released that will require newer versions in that 2018 – 2023 window. For IXP Manager, we are releasing v5 this month and mandating PHP 7.3 support. We do this to stay current and to prevent developer apathy – insisting on legacy frameworks and packages that have been EOL’d provides a major stumbling block for bringing on new developers and contributors. There’s also a real opportunity cost – I have a couple free hours, will I work on project A or project B? If project A uses an old stale toolchain where everything is that much more awkward that project B then which would you choose?

So, from a typical LAMP stack install of Ubuntu 18.04, you’ll find something like the following packages for PHP:

root@ubuntu:/var/www/html# dpkg -l | grep php | cut - -b 1-65
 ii  libapache2-mod-php                    1:7.2+60ubuntu1
 ii  libapache2-mod-php7.2                 7.2.17-0ubuntu0.18.04.1
 ii  php-common                            1:60ubuntu1
 ii  php-mysql                             1:7.2+60ubuntu1
 ii  php7.2-cli                            7.2.17-0ubuntu0.18.04.1
 ii  php7.2-common                         7.2.17-0ubuntu0.18.04.1
 ii  php7.2-json                           7.2.17-0ubuntu0.18.04.1
 ii  php7.2-mysql                          7.2.17-0ubuntu0.18.04.1
 ii  php7.2-opcache                        7.2.17-0ubuntu0.18.04.1
 ii  php7.2-readline                       7.2.17-0ubuntu0.18.04.1

Obviously your exact list will vary depending on what you installed. I find the easiest way to upgrade is to start by removing all installed PHP packages. Based on the above:

dpkg -r libapache2-mod-php libapache2-mod-php7.2 php-common   \
  php-mysql php7.2-cli php7.2-common php7.2-json php7.2-mysql \
  php7.2-opcache php7.2-readline

The goto place for current versions of PHP on Ubuntu is OndÅ™ej Surý’s PPA (Personal Package Archive). OndÅ™ej maintains this in his own time so don’t be afraid to tip him here.

It’s easy to add this to 18.04 as follows:

add-apt-repository ppa:ondrej/php
apt-get update

Then install the PHP 7.3 packages you want / need. For example we can just take the package removal line above and install the 7.3 equivalents with:

apt install libapache2-mod-php libapache2-mod-php7.3 php-common \
    php-mysql php7.3-cli php7.3-common php7.3-json php7.3-mysql \
    php7.3-opcache php7.3-readline

And voilà:

php -v
 PHP 7.3.5-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: May  3 2019 10:00:24) ( NTS )

One post-installation check is to replicate and custom php.ini changes you may have made (max upload size, max post size, max memory usage, etc.).

INEX’s Shiny New Route Servers

Copy of an article I wrote on INEX’s own blog for longevity – original published here on April 10 2019.


In this article, we talk about the new route servers that we deployed across all three peering platforms at INEX during February 2019 and, particularly, RPKI support.

Most route server instances at internet exchanges (IXPs) perform prefix filtering based on route/route6 objects published by internet routing registries (IRRDBs). INEX members would be used to creating these through RIPE’s database. However there are many other registries and the data quality of some of these IRRDB objects is often poor, with problems relating to missing, stale and incorrectly duplicated information.

A typical IRRDB entry would resemble the following:

route:          192.0.2.0/24
descr:          Example IPv4 route object
origin:         AS65500
created:        2004-12-06T11:43:57Z
last-modified:  2016-11-16T22:19:51Z
source:         SOME-IRRDB

RPKI

RPKI is a public key infrastructure framework designed to secure the internet’s routing infrastructure in a way that replaces IRRs with a database where trust is assigned by the resource holder. The equivalent of a route object in RPKI is called a ROA (Route Origin Authorisation). It is a cryptographically secure triplet of information which represents a route, the AS that originates it and the maximum prefix length that may be advertised. An example of an IPv4 and an IPv6 ROA would be:

( Origin AS,  Prefix,         Max Length )
( AS65500,    2001:db8::/32,  /48        )
( AS65501,    192.0.2.0/24,   /24        )

ROAs are typically created through your own RIR (so, RIPE for most INEX members). These RIRs are called trust anchors in RPKI. RIPE have created an extremely easy wizard for creating ROAs through the LIR Portal.

To implement RPKI in a router, the router needs to build and maintain a table of verified ROAs from the five RIRs/trust anchors. The easiest way of doing this is to use a local cache server which pulls and validates the ROAs from the trust anchors and uses a new protocol called RPKI-RTR to feed that information to routers. Currently there are three validators: RIPE’s RPKI Validator v3; Routinator 3000 from NLnetLabs; and Cloudflare’s GoRTR. INEX currently uses the former two.

RPKI validation of a route against the table of ROAs yields one of three possible results:

  • VALID: a ROA exists for the route and both the prefix length is within the allowed range and the origin ASN matches.
  • INVALID: a ROA exists for the route but either (or both) the prefix length is outside the allowed range and/or the origin ASN is different.
  • UNKNOWN: no ROA exists for the route.

UNKNOWN is a common response as the database has only a fraction of the prefix coverage as IRR databases do. We are now in a multi-year transition from IRR to RPKI route validation while ROAs are created.

Bird V2

As well as RPKI support, we have also upgrading all route servers to Bird v2.

This is a significant rewrite to Bird which, for v1, maintained separate code and daemons for IPv4 and IPv6. Bird v2 merges these code bases and also introduces support for new SAFIs such as l3vpns / mpls.

Overall, the configuration changes required were minimal and INEX continues to run separate daemons of Bird v2 for IPv4 and IPv6 daemons. Route servers are CPU intensive and separate daemons allows for maximum stability, keeps the configuration clean and fits into the existing deployment processes we have built up with IXP Manager.

Route Server Filtering Flow

Our work on the new route servers will be released to the community as part of IXP Manager v5 shortly. The new filtering flow is enumerated below. One of the key new features is that if any route fails a step, we use internal large community tagging to indicate this and the specific reason to our members through the IXP Manager looking glass (more on that later).

  1. Filter small prefixes (>/24 for IPv4, >/48 for IPv6).
  2. Filter martian / bogon ranges.
  3. Sanity check to ensure the AS path has at least one ASN and no more than 64.
  4. Sanity check to ensure the peer ASN is the same as first ASN in the prefix’s AS path.
  5. Prevent next-hop hijacking (where a member advertises a route but puts the next hop as another member’s router rather than their own). We do allow same-AS’s to specify their other router(s).
  6. Filter known transit networks.
  7. Ensure that the origin AS is in set of ASNs from member’s AS-SET. See below for some additional detail on this.
  8. RPKI validation. If it is RPKI VALID, accept the route. If it is RPKI INVALID then filter it.
  9. If the route is RPKI UNKNOWN, revert to standard IRRDB filtering.

Regarding step 7 above, an AS-SET is another type of IRRDB database entry where a network which also acts as a transit provider for other networks can enumerate the AS numbers of those downstream networks. This is something RPKI does not yet support but it is being worked on – see AS-Cones.

Lastly we have enhanced the BGP large community support to allow our members request as-path prepends on announcements to specific members over the route servers. For these and more supported communities, see the INEX route server page here.

Bird’s Eye and the Looking Glass

As well as IXP Manager, INEX has also written and open sourced a secure micro service for querying Bird daemons called Bird’s Eye. IXP Manager uses this to provide a web-based looking glass into our route collectors and servers. We have recently released v1.2.1 of Bird’s Eye which adds support for Bird v2.

We have greatly enhanced IXP Manager’s looking glass to support both Bird v2 and the large communities we use to tag filtered reasons. You can explore any of INEX’s route servers to see this yourself – for example this is route server #1 for IPv4 on INEX LAN1. When members log into IXP Manager they will also find a new Filtered Prefixes tool which will summarise any filtered routes across all 12 of INEX’s route server instances.

More Information

We have spoken about this at a number of conferences recently:

Migrating Legacy Web Applications to Laravel

I gave a talk on migrating legacy web applications to Laravel at last year’s Laravel Live UK conference in London. Those that rated it gave it high marks which is always brilliant feedback to receive.

2018 was Laravel Live UK’s inaugural conference and it was a packed house. They’ve just announced the dates for 2019 and I would strongly recommend attending for anyone using or interested in use Laravel.

Following the conference, I wrote up the talk as an article and it has just been published in the March 2019 edition of php[architect]. This is an excellent magazine which I’ve subscribed to for a few years now – the digital edition is very reasonable and comes as a DRM-free PDF onto an app on your phone/pad or downloaded to your computer.

As it happens, they chose this article as the teaser for this issue and so it is freely available online here and downloadable as a PDF here. But seriously, if you are a PHP developer, you need to subscribe to this magazine.

Lastly, if you are interested in the slide deck from the conference, you can download them here – but the article is a much better way to understand this material.

Switching Between PHP Versions on MacOS

When switching between different PHP projects (or different branches of one PHP project), you often need to switch PHP versions. Particularly as older libraries will not run on newer versions of PHP. After losing patience with how the otherwise excellent Homebrew handles this, I stumbled upon this super-useful tool: switch-php.

This seems to have been written for PHP developers and integrates brilliantly with Laravel Valet. Here’s an example of switching from PHP 7.3 to 7.2:

$ php -v 
PHP 7.3.0 (cli) (built: Dec  7 2018 11:00:11) ( NTS ) 
Copyright (c) 1997-2018 The PHP Group 

$ switch-php 7.2 
Valet stopped ✔ 
PHP switched ✔ 
Valet started ✔ 

You are now using PHP 7.2.13 

$ php -v 
PHP 7.2.13 (cli) (built: Dec  7 2018 10:41:23) ( NTS ) 
Copyright (c) 1997-2018 The PHP Group

Three Rock in the Fog

A quick New Year’s walk up to Three Rock and the Fairy Castle in the fog this morning.


UI Tests with Laravel Dusk for IXP Manager

We use standard PHPUnit tests for IXP Manager for some mission critical aspects. These take data from a test database filled with known sample data (representing a range of different member configurations). The tests then use this information to generate configurations and compare these against known-good configurations.

This way, we know that for a given set of data, we will get a predictable output so long as we haven’t accidentally broken anything in development.

But, as an end user, how do you know that what you stick in a web-based form gets put into the database correctly? And conversely, how do you know that form represents the database data correctly when editing?

This is an issue we ran into recently around some checkbox logic and a dropdown showing the wrong selected element. These issues are every bit as dangerous to mission critical elements as the output tests we do with PHPUnit.

To test the frontend, we turn to Laravel Duskan expressive, easy-to-use browser automation and testing API. What this actually means is that we can right code like this:

$this->browse( function( $browser ) use ( $user ) {
    $browser->visit('/login')
        ->type( 'username', $user->email )
        ->type( 'password', 'secret' )
        ->press('Login')
        ->assertPathIs('/home')
        ->assertSee( 'Welcome to IXP Manager ' . $user->name );

We have now added Dusk tests for UI elements that involve adding, editing and deleting all aspects of a member interface and all aspects of adding, editing and delete a router object. Here’s an example of the latter:

Laravel Dusk - Animated Gif Example

Doctrine2 with GROUP_CONCAT and non-related JOIN

Doctrine2 ORM is a fantastic and powerful object relational mapper (ORM) for PHP. We use it for IXP Manager to great effect and we only support MySQL so our hands are not tied to pure Doctrine2 DQL supported functions. We also use the excellent Laravel Doctrine project with the Berberlei extensions.

Sometimes time is against you as a developer and the documentation (and StackOverflow!) lacks the obvious solutions you need and you end up solving what could be a single elegant query very inefficiently in code with iterative database queries. Yuck. 

I spent a bit of time last night trying to unravel one very bad example of this where the solution would require DQL that could:

  1. group / concatenate multiple column results from a one-to-many relationship;
  2. join a table without a relationship;
  3. ensure the joining of the table without the relationship would not exclude results where the joint table had no matches;
  4. provide a default value for (3).

Each of these was solved as follows:

  1. via MySQL’s GROUP_CONCAT() aggregator. The specific example here is that when a MAC address associated with a virtual interface can be visible in multiple switch ports. We want to present the switch ports to the user and GROUP_CONCAT() allows us to aggregate these as a comma separated concatenated string (e.g. "Ethernet1,Ethernet8,Ethernet9").
  2. Normally with Doctrine2, all relationships would be well-defined with foreign keys. This is not always practical and sometimes we need to join tables on the result of some equality test. We can do this using a DQL construct such as: JOIN Entities\OUI o WITH SUBSTRING( m.mac, 1, 6 ) = o.oui.
  3. This is as simple as ensuring you LEFT JOIN.
  4. The COALESCE() function is used for this: COALESCE( o.organisation, 'Unknown' ) AS organisation.

We have not yet pushed the updated code into IXP Manager mainline but the above referenced function / code is not replaced with the DQL query:

SELECT m.mac AS mac, vi.id AS viid, m.id AS id, 
    m.firstseen AS firstseen, m.lastseen AS lastseen,  
    c.id AS customerid, c.abbreviatedName AS customer,
    s.name AS switchname, 
    GROUP_CONCAT( sp.name ) AS switchport, 
    GROUP_CONCAT( DISTINCT ipv4.address ) AS ip4, 
    GROUP_CONCAT( DISTINCT ipv6.address ) AS ip6,
    COALESCE( o.organisation, 'Unknown' ) AS organisation

FROM Entities\\MACAddress m
    JOIN m.VirtualInterface vi
    JOIN vi.VlanInterfaces vli
    LEFT JOIN vli.IPv4Address ipv4
    LEFT JOIN vli.IPv6Address ipv6
    JOIN vi.Customer c
    LEFT JOIN vi.PhysicalInterfaces pi
    LEFT JOIN pi.SwitchPort sp
    LEFT JOIN sp.Switcher s
    LEFT JOIN Entities\\OUI o WITH SUBSTRING( m.mac, 1, 6 ) = o.oui

GROUP BY m.mac, vi.id, m.id, m.firstseen, m.lastseen, 
    c.id, c.abbreviatedName, s.name, o.organisation

ORDER BY c.abbreviatedName ASC

 

Evaluating zsh

I’ve always been a bash user but I’ve recently decided to give zsh a while. It has some pretty useful features such as path expansion and replacement (see this slideshare). And yes, I’m well aware of bash-completion thank you very much.

It also has a nice eco system of expansions including oh-my-zsh with which I’m using plugins for git, composer (php), laravel5, brew, bower, vagrant, node and npm. I went with the agnoster theme and for iTerm2 (my terminal application of choice) I installed the Solarized Dark and Light themes. Both work well with the agnoster theme. I also installed and use the Meslo font.

One issue I did find immediately is things like file type colourisation with ls were not as good as bash. To resolve this, I installed the warhol plugin (as well as brew install zsh-syntax-highlighting grc). Now I find my ls’, ping’s, traceroute’s etc all nicely coloured.

We use Dropbox with work and to keep my work and home office laptops in sync, I moved the configs into Dropbox and symlinked to them:

cd ~
mv .oh-my-zsh Dropbox/ 
mv .zshrc Dropbox
ln -s Dropbox/.og-my-zsh
ln -s Dropbox/.zshrc

This all works really well. My bash aliases are fully compatible so I just pull them in at the end of .zshrc (source ~/.bash_aliases). Lastly – to prevent the prompt including my username and hostname on my local laptop, I set the following in .zshrc:

export DEFAULT_USER=barryo

So far, so happy.

PhpStorm and Xdebug – macOS / Homebrew

After many years of Sublime Text and, latterly, Atom, I’ve decided to give an integrated IDE another look – this time PhpStorm. I’ve always dropped them in the past as they tended to crash (hello Zend Studio) and were slow as hell (hello again Zend Studio). But so far so good – I’m only a couple days into an evaluation license but it’s fast (admittedly I have fast laptops – Intel i7’s with four cores, PCI SSD and 16GB RAM) and it’s yet to crash.

One of the key advantages of IDE’s is integrated debugging. This was ridiculously easy with PhpStorm. I use Homebrew for PHP:

$ brew ls --full-name
 homebrew/completions/composer-completion
 homebrew/php/composer
 homebrew/php/php71
 homebrew/php/php71-intl
 homebrew/php/php71-mcrypt
 homebrew/php/php71-xdebug

I’ve then configured xdebug as follows:

$ cat /usr/local/etc/php/7.1/conf.d/ext-xdebug.ini
[xdebug]
zend_extension="/usr/local/opt/php71-xdebug/xdebug.so"
xdebug.remote_enable=1
xdebug.remote_host=localhost
xdebug.remote_port=9001
xdebug.remote_autostart=1
xdebug.idekey=PHPSTORM

If you’re not using Laravel’s Valet for local development then you should check it out immediately: https://laravel.com/docs/5.3/valet. If you are using it, issue a valet restart.

Port 9001 was chosen above as Valet tends to use 9000 also. We now need to reconfigure PhpStorm to list on this port. Open preferences and type xdebug into the search box. Then find Languages & Frameworks -> PHP -> Debug on the left hand navigation pane and change the port to 9001.

That’s pretty much it for PhpStorm. They really mean zero-configuration debugging. When editing a project in the IDE, there’s a Start Listening for PHP Debug Connections toggle icon in the top left – it looks like a phone. Just turn it on.

The last thing we need to do is have an easy way to enable Xdebug when we want it when testing our applications in the browser. Chrome has a very useful plugin for this: Xdebug-helper. Just install it and edit its options and change the IDE form Eclipse to PhpStorm. You can now use this to start a debug session from within Chrome to your listening PhpStorm IDE.

Oh, just found this useful resource also covering similar topics with a CGI/CLI xdebug split.