LUG Community Blogs

Chris Lamb: Free software activities in November 2016

Planet ALUG - Wed, 30/11/2016 - 21:18

Here is my monthly update covering what I have been doing in the free software world (previous month):

  • Started work on a Python API to the UK Postbox mail scanning and forwarding service. (repo)
  • Lots of improvements to buildinfo.debian.net, my experiment into how to process, store and distribute .buildinfo files after the Debian archive software has processed them, including making GPG signatures mandatory (#7), updating jenkins.debian.net to sign them and moving to SSL.
  • Improved the Django client to the KeyError error tracking software, enlarging the test coverage and additionally adding support for grouping errors using a context manager.
  • Made a number of improvements to travis.debian.net, my hosted service for projects that host their Debian packaging on GitHub to use the Travis CI continuous integration platform to test builds on every code change:
    • Install build-dependencies with debugging output. Thanks to @waja. (#31)
    • Install Lintian by default. Thanks to @freeekanayaka. (#33).
    • Call mktemp with --dry-run to avoid having to delete it later. (commit)
  • Submitted a pull request to Wheel (a utility to package Python libraries) to make the output of METADATA files reproducible. (#73)
  • Submitted some miscellaneous documentation updates to the Tails operating system. (patches)
Reproducible builds

Whilst anyone can inspect the source code of free software for malicious flaws, most software is distributed pre-compiled to end users.

The motivation behind the Reproducible Builds effort is to permit verification that no flaws have been introduced — either maliciously or accidentally — during this compilation process by promising identical results are always generated from a given source, thus allowing multiple third-parties to come to a consensus on whether a build was compromised.


This month:


My work in the Reproducible Builds project was also covered in our weekly reports. (#80, #81, #82 #83.


Toolchain issues

I submitted the following patches to fix reproducibility-related toolchain issues with Debian:


strip-nondeterminism

strip-nondeterminism is our tool to remove specific non-deterministic results from a completed build.


jenkins.debian.net

jenkins.debian.net runs our comprehensive testing framework.

  • buildinfo.debian.net has moved to SSL. (ac3b9e7)
  • Submit signing keys to keyservers after generation. (bdee6ff)
  • Various cosmetic changes, including
    • Prefer if X not in Y over if not X in Y. (bc23884)
    • No need for a dictionary; let's just use a set. (bf3fb6c)
    • Avoid DRY violation by using a for loop. (4125ec5)

I also submitted 9 patches to fix specific reproducibility issues in apktool, cairo-5c, lava-dispatcher, lava-server, node-rimraf, perlbrew, qsynth, tunnelx & zp.

Debian
Debian LTS

This month I have been paid to work 11 hours on Debian Long Term Support (LTS). In that time I did the following:

  • "Frontdesk" duties, triaging CVEs, etc.
  • Issued DLA 697-1 for bsdiff fixing an arbitrary write vulnerability.
  • Issued DLA 705-1 for python-imaging correcting a number of memory overflow issues.
  • Issued DLA 713-1 for sniffit where a buffer overflow allowed a specially-crafted configuration file to provide a root shell.
  • Issued DLA 723-1 for libsoap-lite-perl preventing a Billion Laughs XML expansion attack.
  • Issued DLA 724-1 for mcabber fixing a roster push attack.
Uploads
  • redis:
    • 3.2.5-2 — Tighten permissions of /var/{lib,log}/redis. (#842987)
    • 3.2.5-3 & 3.2.5-4 — Improve autopkgtest tests and install upstream's MANIFESTO and README.md documentation.
  • gunicorn (19.6.0-9) — Adding autopkgtest tests.
  • libfiu:
    • 0.94-1 — Add autopkgtest tests.
    • 0.95-1, 0.95-2 & 0.95-3 — New upstream release and improve autopkgtest coverage.
  • python-django (1.10.3-1) — New upstream release.
  • aptfs (0.8-3, 0.8-4 & 0.8-5) — Adding and subsequently improving the autopkgtext tests.


I performed the following QA uploads:



Finally, I also made the following non-maintainer uploads:

  • libident (0.22-3.1) — Move from obsolete Source-Version substvar to binary:Version. (#833195)
  • libpcl1 (1.6-1.1) — Move from obsolete Source-Version substvar to binary:Version. (#833196)
  • pygopherd (2.0.18.4+nmu1) — Move from obsolete Source-Version substvar to ${source:Version}. (#833202)
Debian bugs filed RC bugs

I also filed 59 FTBFS bugs against arc-gui-clients, asyncpg, blhc, civicrm, d-feet, dpdk, fbpanel, freeciv, freeplane, gant, golang-github-googleapis-gax-go, golang-github-googleapis-proto-client-go, haskell-cabal-install, haskell-fail, haskell-monadcatchio-transformers, hg-git, htsjdk, hyperscan, jasperreports, json-simple, keystone, koji, libapache-mod-musicindex, libcoap, libdr-tarantool-perl, libmath-bigint-gmp-perl, libpng1.6, link-grammar, lua-sql, mediatomb, mitmproxy, ncrack, net-tools, node-dateformat, node-fuzzaldrin-plus, node-nopt, open-infrastructure-system-images, open-infrastructure-system-images, photofloat, ppp, ptlib, python-mpop, python-mysqldb, python-passlib, python-protobix, python-ttystatus, redland, ros-message-generation, ruby-ethon, ruby-nokogiri, salt-formula-ceilometer, spykeviewer, sssd, suil, torus-trooper, trash-cli, twisted-web2, uftp & wide-dhcpv6.

FTP Team

As a Debian FTP assistant I ACCEPTed 70 packages: bbqsql, coz-profiler, cross-toolchain-base, cross-toolchain-base-ports, dgit-test-dummy, django-anymail, django-hstore, django-html-sanitizer, django-impersonate, django-wkhtmltopdf, gcc-6-cross, gcc-defaults, gnome-shell-extension-dashtodock, golang-defaults, golang-github-btcsuite-fastsha256, golang-github-dnephin-cobra, golang-github-docker-go-events, golang-github-gogits-cron, golang-github-opencontainers-image-spec, haskell-debian, kpmcore, libdancer-logger-syslog-perl, libmoox-buildargs-perl, libmoox-role-cloneset-perl, libreoffice, linux-firmware-raspi3, linux-latest, node-babel-runtime, node-big.js, node-buffer-shims, node-charm, node-cliui, node-core-js, node-cpr, node-difflet, node-doctrine, node-duplexer2, node-emojis-list, node-eslint-plugin-flowtype, node-everything.js, node-execa, node-grunt-contrib-coffee, node-grunt-contrib-concat, node-jquery-textcomplete, node-js-tokens, node-json5, node-jsonfile, node-marked-man, node-os-locale, node-sparkles, node-tap-parser, node-time-stamp, node-wrap-ansi, ooniprobe, policycoreutils, pybind11, pygresql, pysynphot, python-axolotl, python-drizzle, python-geoip2, python-mockupdb, python-pyforge, python-sentinels, python-waiting, pythonmagick, r-cran-isocodes, ruby-unicode-display-width, suricata & voctomix-outcasts.

I additionally filed 4 RC bugs against packages that had incomplete debian/copyright files against node-cliui, node-core-js, node-cpr & node-grunt-contrib-concat.

Categories: LUG Community Blogs

Andy Smith: Supermicro SATA DOM flash devices don’t report lifetime writes correctly

Planet HantsLUG - Sat, 26/11/2016 - 16:43

I’m playing around with a pair of Supermicro SATA DOM flash devices at the moment, evaluating them for use as the operating system storage for servers (as opposed to where customer data goes).

They’re flash devices with a limited write endurance. The smallest model (16GB), for example, is good for 17TB of writes. Therefore it’s important to know how much you’ve actually written to it.

Many SSDs and other flash devices expose the total amount written through the SMART attribute 241, Total_LBAs_Written. The SATA DOM devices do seem to expose this attribute, but right now they say this:

$ for dom in $(sudo lsblk --paths -d -o NAME,MODEL --noheadings | awk '/SATA SSD/ { print $1 }') do echo -n "$dom: " sudo smartctl -A "$dom" | awk '/^241/ { print $10 * 512 * 1.0e-9, "GB" }' done /dev/sda: 0.00856934 GB /dev/sdb: 0.00881715 GB

This being after install and (as of now) more than a week of uptime, ~9MB of lifetime writes isn’t credible.

Another place we can look for amount of bytes written is /proc/diskstats. The 10th column is the number of (512-byte) sectors written, so:

$ for dom in $(sudo lsblk -d -o NAME,MODEL --noheadings | awk '/SATA SSD/ { print $1 }') do awk "/$dom / { print \$3, \$10 / 2 * 1.0e-6, \"GB\" }" /proc/diskstats done sda 3.93009 GB sdb 3.93009 GB

Almost 4GB is a lot more believable, so can we just use /proc/diskstats? Well, the problem there is that those figures are only since boot. That won’t include, for example, all the data written during install.

Okay, so, are these figures even consistent? Let’s write 100MB and see what changes.

Since the figure provided by SMART attribute 241 apparently isn’t actually 512-byte blocks we’ll just print the raw value there.

Before:

$ for dom in $(sudo lsblk -d -o NAME,MODEL --noheadings | awk '/SATA SSD/ { print $1 }') do awk "/$dom / { print \$3, \$10 / 2 * 1.0e-6, \"GB\" }" /proc/diskstats done sda 4.03076 GB sdb 4.03076 GB $ for dom in $(sudo lsblk --paths -d -o NAME,MODEL --noheadings | awk '/SATA SSD/ { print $1 }') do echo -n "$dom: " sudo smartctl -A "$dom" | awk '/^241/ { print $10 }' done /dev/sda: 16835 /dev/sdb: 17318

Write 100MB:

$ dd if=/dev/urandom bs=1MB count=100 > /var/tmp/one_hundred_megabytes 100+0 records in 100+0 records out 100000000 bytes (100 MB) copied, 7.40454 s, 13.5 MB/s

(I used /dev/urandom just in case some compression might take place or something)

After:

$ for dom in $(sudo lsblk -d -o NAME,MODEL --noheadings | awk '/SATA SSD/ { print $1 }') do awk "/$dom / { print \$3, \$10 / 2 * 1.0e-6, \"GB\" }" /proc/diskstats done sda 4.13046 GB sdb 4.13046 GB $ for dom in $(sudo lsblk --paths -d -o NAME,MODEL --noheadings | awk '/SATA SSD/ { print $1 }') do echo -n "$dom: " sudo smartctl -A "$dom" | awk '/^241/ { print $10 }' done /dev/sda: 16932 /dev/sdb: 17416

Well, alright, all is apparently not lost: SMART attribute 241 went up by ~100 and diskstats agrees that ~100MB was written too, so it looks like it does actually report lifetime writes, but it’s reporting them as megabytes (109 bytes), not 512-byte sectors.

Every reference I can find says that Total_LBAs_Written is the number of 512-byte sectors, though, so in reporting units of 1MB I feel that these devices are doing the wrong thing.

Anyway, I’m a little alarmed that ~0.1% of the lifetime has gone already, although a lot of that would have been the install. I probably should take this opportunity to get rid of a lot of writes by tracking down logging of mundane garbage. Also this is the smallest model; the devices are rated for 1 DWPD so just over-provisioning by using a larger model than necessary will help.

Categories: LUG Community Blogs

Steve Engledow (stilvoid): Win or lose?

Planet ALUG - Wed, 23/11/2016 - 01:02

I never paid any attention in art classes. On reflection, I think we had an awful teacher who more or less ignored those of us with no latent talent or interest. I grew up mildly jealous of people I knew who could draw and always wished I was able.

Over the past few years, I've heard several people say that artistic ability is 10% talent and 90% practice and I've considered giving it a go at some point. Recently, we bought some pencils and a pad for my son and this evening, with a glass of wine at hand and some 70s rock on the stereo, I decided to take the plunge and see what horrors I could submit the unwitting page to.

Here's the first thing I've drawn since school:

It was supposed to be my wife. If you know her, you'll know I failed ;)

I focussed too much on the individual features and not enough on the overall shape. The eyes and hair aren't bad (at least they look something like hers), but the mouth and nose are too large and disproportionate - though recognisable.

I decided to try drawing what was in front of me: a ghost-shaped candle holder:

That's a photo by the way, not my drawing ;)

Here's the drawing. I killed the perspective somewhat but at least it's recognisable!

After I'd drawn the ghost, I decided to have another go at my wife while she wasn't paying attention. This one looks more like her but the eyes look as though she's been in a fight and the hair is a tad more Edward Scissorhands than I'd intended.

Overall, I got a better result than I'd expected from my first three attempts at sketching in 20 years. This might turn into a series.

More than willing to receive criticism and advice from people who know what they're doing with a pencil :)

Categories: LUG Community Blogs

Steve Engledow (stilvoid): Win or lose?

Planet ALUG - Wed, 23/11/2016 - 01:02

I never paid any attention in art classes. On reflection, I think we had an awful teacher who more or less ignored those of us with no latent talent or interest. I grew up mildly jealous of people I knew who could draw and always wished I was able.

Over the past few years, I've heard several people say that artistic ability is 10% talent and 90% practice and I've considered giving it a go at some point. Recently, we bought some pencils and a pad for my son and this evening, with a glass of wine at hand and some 70s rock on the stereo, I decided to take the plunge and see what horrors I could submit the unwitting page to.

Here's the first thing I've drawn since school:

It was supposed to be my wife. If you know her, you'll know I failed ;)

I focussed too much on the individual features and not enough on the overall shape. The eyes and hair aren't bad (at least they look something like hers), but the mouth and nose are too large and disproportionate - though recognisable.

I decided to try drawing what was in front of me: a ghost-shaped candle holder:

That's a photo by the way, not my drawing ;)

Here's the drawing. I killed the perspective somewhat but at least it's recognisable!

After I'd drawn the ghost, I decided to have another go at my wife while she wasn't paying attention. This one looks more like her but the eyes look as though she's been in a fight and the hair is a tad more Edward Scissorhands than I'd intended.

Overall, I got a better result than I'd expected from my first three attempts at sketching in 20 years. This might turn into a series.

More than willing to receive criticism and advice from people who know what they're doing with a pencil :)

Categories: LUG Community Blogs

Steve Engledow (stilvoid): Win or lose?

Planet ALUG - Wed, 23/11/2016 - 01:02

I never paid any attention in art classes. On reflection, I think we had an awful teacher who more or less ignored those of us with no latent talent or interest. I grew up mildly jealous of people I knew who could draw and always wished I was able.

Over the past few years, I've heard several people say that artistic ability is 10% talent and 90% practice and I've considered giving it a go at some point. Recently, we bought some pencils and a pad for my son and this evening, with a glass of wine at hand and some 70s rock on the stereo, I decided to take the plunge and see what horrors I could submit the unwitting page to.

Here's the first thing I've drawn since school:

It was supposed to be my wife. If you know her, you'll know I failed ;)

I focussed too much on the individual features and not enough on the overall shape. The eyes and hair aren't bad (at least they look something like hers), but the mouth and nose are too large and disproportionate - though recognisable.

I decided to try drawing what was in front of me: a ghost-shaped candle holder:

That's a photo by the way, not my drawing ;)

Here's the drawing. I killed the perspective somewhat but at least it's recognisable!

After I'd drawn the ghost, I decided to have another go at my wife while she wasn't paying attention. This one looks more like her but the eyes look as though she's been in a fight and the hair is a tad more Edward Scissorhands than I'd intended.

Overall, I got a better result than I'd expected from my first three attempts at sketching in 20 years. This might turn into a series.

More than willing to receive criticism and advice from people who know what they're doing with a pencil :)

Categories: LUG Community Blogs

Steve Kemp: Detecting fraudulent signups?

Planet HantsLUG - Mon, 21/11/2016 - 05:37

I run a couple of different sites that allow users to sign-up and use various services. In each of these sites I have some minimal rules in place to detect bad signups, but these are a little ad hoc, because the nature of "badness" varies on a per-site basis.

I've worked in a couple of places where there are in-house tests of bad signups, and these usually boil down to some naive, and overly-broad, rules:

  • Does the phone numbers' (international) prefix match the country of the user?
  • Does the postal address supplied even exist?

Some places penalise users based upon location too:

  • Does the IP address the user submitted from come from TOR?
  • Does the geo-IP country match the users' stated location?
  • Is the email address provided by a "free" provider?

At the moment I've got a simple HTTP-server which receives a JSON post of a new users' details, and returns "200 OK" or "403 Forbidden" based on some very very simple critereon. This is modeled on the spam detection service for blog-comments server I use - something that is itself becoming less useful over time. (Perhaps time to kill that? A decision for another day.)

Unfortunately this whole approach is very reactive, as it takes human eyeballs to detect new classes of problems. Code can't guess in advance that it should block usernames which could collide with official ones, for example allowing a username of "admin", "help", or "support".

I'm certain that these systems have been written a thousand times, as I've seen at least five such systems, and they're all very similar. The biggest flaw in all these systems is that they try to classify users in advance of them doing anything. We're trying to say "Block users who will use stolen credit cards", or "Block users who'll submit spam", by correlating that behaviour with other things. In an ideal world you'd judge users only by the actions they take, not how they signed up. And yet .. it is better than nothing.

For the moment I'm continuing to try to make the best of things, at least by centralising the rules for myself I cut down on duplicate code. I'll pretend I'm being cool, modern, and sexy, and call this a micro-service! (Ignore the lack of containers for the moment!)

Categories: LUG Community Blogs

Debian Bits: Debian Contributors Survey 2016

Planet HantsLUG - Wed, 16/11/2016 - 14:45

The Debian Contributor Survey launched last week!

In order to better understand and document who contributes to Debian, we (Mathieu ONeil, Molly de Blanc, and Stefano Zacchiroli) have created this survey to capture the current state of participation in the Debian Project through the lense of common demographics. We hope a general survey will become an annual effort, and that each year there will also be a focus on a specific aspect of the project or community. The 2016 edition contains sections concerning work, employment, and labour issues in order to learn about who is getting paid to work on and with Debian, and how those relationships affect contributions.

We want to hear from as many Debian contributors as possible—whether you've submitted a bug report, attended a DebConf, reviewed translations, maintain packages, participated in Debian teams, or are a Debian Developer. Completing the survey should take 10-30 minutes, depending on your current involvement with the project and employment status.

In an effort to reflect our own ideals as well as those of the Debian project, we are using LimeSurvey, an entirely free software survey tool, in an instance of it hosted by the LimeSurvey developers.

Survey responses are anonymous, IP and HTTP information are not logged, and all questions are optional. As it is still likely possible to determine who a respondent is based on their answers, results will only be distributed in aggregate form, in a way that does not allow deanonymization. The results of the survey will be analyzed as part of ongoing research work by the organizers. A report discussing the results will be published under a DFSG-free license and distributed to the Debian community as soon as it's ready. The raw, disaggregated answers will not be distributed and will be kept under the responsibility of the organizers.

We hope you will fill out the Debian Contributor Survey. The deadline for participation is: 4 December 2016, at 23:59 UTC.

If you have any questions, don't hesitate to contact us via email at:

Categories: LUG Community Blogs

Mick Morgan: if it be your will

Planet ALUG - Fri, 11/11/2016 - 17:30

A bleak week just got worse. The results of the US Presidential election are, frankly, beyond belief. We now have a xenophobic, racist, misogynistic megalomaniac waiting to move into the White House and become, literally, the most powerful man on earth.

And now Leonard Cohen has died.

Cohen is one of my all time favourite artists. A writer of beautiful poetry and lyrics beyond compare and endowed with a voice capable of moving me to tears. I cry now because that voice is silenced.

In the mid eighties he wrote “If it be your will” which starts:

If it be your will
That I speak no more
And my voice be still
As it was before
I will speak no more
I shall abide until
I am spoken for
If it be your will

This year he wrote in “You want it darker

If You are the dealer, I’m out of the game
If You are the healer, I’m broken and lame
If Thine is the glory, then mine must be the shame
You want it darker – we kill the flame.
Magnified, sanctified is your holy name
Vilified, crucified in the human frame
A million candles burning for the help that never came
You want it darker – Hineni, Hineni, I’m ready, my Lord.

Now he is gone, in the same week the US voted a dangerous buffoon to the Presidency. If there be a God, he has a cruel sense of humour. The world has just got darker.

Categories: LUG Community Blogs

Chris Lamb: Awarded Core Infrastructure Initiative grant for Reproducible Builds

Planet ALUG - Fri, 11/11/2016 - 17:04

I'm delighted to announce that I have been awarded a grant from the Core Infrastructure Initiative (CII) to fund my previously-voluntary work on Reproducible Builds.

Whilst anyone can inspect the source code of free software for malicious flaws, most software is distributed pre-compiled to end users. The motivation behind the Reproducible Builds effort is to permit verification that no flaws have been introduced — either maliciously or accidentally — during this compilation process by promising identical results are always generated from a given source, thus allowing multiple third-parties to come to a consensus on whether a build was compromised.

I'd like to sincerely thank the CII, not only for their material support but also for their recognition of my existing contributions. I am looking forward to working with my co-grantees towards fulfilling our shared goal.

You can read the CII's press release here.

Categories: LUG Community Blogs

Chris Lamb: Core Infrastructure Initiative grant for Reproducible Builds

Planet ALUG - Fri, 11/11/2016 - 17:01

I'm delighted to announce that I have been awarded a grant from the Core Infrastructure Initiative (CII) to fund my previously-voluntary work on Reproducible Builds.

Whilst anyone can inspect the original source code of free software for malicious flaws, most GNU/Linux distributions provide pre-compiled software to end users. The motivation behind the Reproducible Builds effort is to allow verification that no flaws have been introduced — either maliciously or accidentally — during this compilation process by promising identical binary packages are always generated from a given source.

I'd like to sincerely thank the CII, not only for their material support but also for their recognition of my existing contributions. I am looking forward to working with my co-grantees towards fulfilling our shared goal.

Press release.

Categories: LUG Community Blogs

Steve Engledow (stilvoid): Rye, oh rye?

Planet ALUG - Fri, 04/11/2016 - 16:36

A few months ago, I signed up for Flavourly which delivers me different beers every month from small breweries. I've been tucking in to this month's batch and, as I was sitting at my laptop, the beer I'd just opened made me want to review it which is something I've never done before. Excuse my indulgence ;)

Battersea Rye from Sambrook's brewery.

The first words out of my mouth after pouring some of this into my Norwich beer festival 2015 glass and giving it a distracted sip were "ooh, this is nice" which, speaking as a Brit, is high praise. It's these moments I live for when trying new beers; when the first sip is taken while I've got my mind on other things - in this case I was reading a requirements doc - and the taste just takes over making me forget what I was doing - in a good way.

Now that I've paused for a few moments to write that first paragraph, I've just taken a second, more deliberate swig. The initial surprise is out of the way and I can see that there's depth beyond the first sip. It's malty, which I'd expected, but fruity too, which I hadn't - although looking at the label now, I notice it bears a tagline of "bold spicy fruit".

A few moments after that second sip, I can still feel the malt rolling around in my mouth. Time for a third...

Still good but now I'm noticing the strength (I just checked, it's 5.8%). I think the rest of this bottle is going to go down very nicely. I've been suckered into the recent popularity of pale ales and haven't drunk much that's brown for months so this is a very pleasant change and it's particularly nice not to be assaulted by the overly malty taste that some darker brews bring to the table.

Half the bottle down and this is definitely living up to the "bold" part of its tagline which suits me just fine; I'm a fan of stronger beers generally. Give me some Good King Henry any day of the week and I'm a happy man. The fruitiness is starting to dissipate and giving way to a foamy mouthfeel that I'm willing to look past. A large gulp brings back the fruity taste as I let the beer swill around. At the risk of sounding like a wine taster, there's cherry, dates, and perhaps fig there.

All in all, I'm thoroughly enjoying this beer. It crossed my mind briefly that perhaps it would be better if it had less fizz and was slightly less alcoholic but on reflection, as I near the bottom of the glass, I think that would take away from the balance.

I'd give this a rating but they're only of any use against other ratings and this is the first beer I've reviewed ;)

If it helps, my wife, who generally only drinks pale ales, said "hmm, very nice".

Categories: LUG Community Blogs

Steve Engledow (stilvoid): Rye, oh rye?

Planet ALUG - Fri, 04/11/2016 - 16:36

A few months ago, I signed up for Flavourly which delivers me different beers every month from small breweries. I've been tucking in to this month's batch and, as I was sitting at my laptop, the beer I'd just opened made me want to review it which is something I've never done before. Excuse my indulgence ;)

Battersea Rye from Sambrook's brewery.

The first words out of my mouth after pouring some of this into my Norwich beer festival 2015 glass and giving it a distracted sip were "ooh, this is nice" which, speaking as a Brit, is high praise. It's these moments I live for when trying new beers; when the first sip is taken while I've got my mind on other things - in this case I was reading a requirements doc - and the taste just takes over making me forget what I was doing - in a good way.

Now that I've paused for a few moments to write that first paragraph, I've just taken a second, more deliberate swig. The initial surprise is out of the way and I can see that there's depth beyond the first sip. It's malty, which I'd expected, but fruity too, which I hadn't - although looking at the label now, I notice it bears a tagline of "bold spicy fruit".

A few moments after that second sip, I can still feel the malt rolling around in my mouth. Time for a third...

Still good but now I'm noticing the strength (I just checked, it's 5.8%). I think the rest of this bottle is going to go down very nicely. I've been suckered into the recent popularity of pale ales and haven't drunk much that's brown for months so this is a very pleasant change and it's particularly nice not to be assaulted by the overly malty taste that some darker brews bring to the table.

Half the bottle down and this is definitely living up to the "bold" part of its tagline which suits me just fine; I'm a fan of stronger beers generally. Give me some Good King Henry any day of the week and I'm a happy man. The fruitiness is starting to dissipate and giving way to a foamy mouthfeel that I'm willing to look past. A large gulp brings back the fruity taste as I let the beer swill around. At the risk of sounding like a wine taster, there's cherry, dates, and perhaps fig there.

All in all, I'm thoroughly enjoying this beer. It crossed my mind briefly that perhaps it would be better if it had less fizz and was slightly less alcoholic but on reflection, as I near the bottom of the glass, I think that would take away from the balance.

I'd give this a rating but they're only of any use against other ratings and this is the first beer I've reviewed ;)

If it helps, my wife, who generally only drinks pale ales, said "hmm, very nice".

Categories: LUG Community Blogs

Debian Bits: New Debian Developers and Maintainers (September and October 2016)

Planet HantsLUG - Thu, 03/11/2016 - 11:00

The following contributors got their Debian Developer accounts in the last two months:

  • Adriano Rafael Gomes (adrianorg)
  • Arturo Borrero González (arturo)
  • Sandro Knauß (hefee)

The following contributors were added as Debian Maintainers in the last two months:

  • Abhijith PA
  • Mo Zhou
  • Víctor Cuadrado Juan
  • Zygmunt Bazyli Krynicki
  • Robert Haist
  • Sunil Mohan Adapa
  • Elena Grandi
  • Eric Heintzmann
  • Dylan Aïssi
  • Daniel Shahaf
  • Samuel Henrique
  • Kai-Chung Yan
  • Tino Mettler

Congratulations!

Categories: LUG Community Blogs

Chris Lamb: Free software activities in October 2016

Planet ALUG - Mon, 31/10/2016 - 20:48

Here is my monthly update covering what I have been doing in the free software world (previously):

  • Made a large number of improvements to travis.debian.net, my hosted service for projects that host their Debian packaging on GitHub to use the Travis CI continuous integration platform to test builds on every code change:
    • Enabled the use of Git submodules. Thanks to @unera & @hosiet. (#30)
    • Managed a contribution from @xhaakon to allow adding an extra repository for custom dependencies. (#17)
    • Fixed an issue where builds did not work under Debian Wheezy or Ubuntu Trusty due to a call to dpkg-buildpackage --show-field. (#28)
    • Fixed an issue where TRAVIS_DEBIAN_EXTRA_REPOSITORY was accidentally required. (#27)
    • Made a number of miscellaneous cosmetic improvements. (f7e5b080 & 037de91cc, etc.)
  • Submitted a pull request to Alabaster, the default theme for the Python Sphinx documentation system, to ensure that "extra navigation links" are rendered reproducibly. (#90)
  • Improved my Chrome extension for the FastMail web interface:
    • Managed a pull request from @jlerner to add an optional confirmation dialogue before sending any message. (#10)
    • Added an optional Ctrl+Enter alias for Alt+Enter to limit searches to the current folder; the latter shortcut is already mapped by my window manager. (d691b07)
    • Various cosmetic changes to the options page. (7b95e887 & 833ff0fe)
  • Submitted two pull requests to mypy, an experimental static type checker for Python:
    • Ensure that the output of --usage is reproducible. (#2234)
    • Update the --usage output to match the — now-reproducible — output. (#2235)
  • Updated django-slack, my library to easily post messages to the Slack group-messaging utility:
    • Merged a feature from @lvpython to add an option to post the message as the authenticated user rather than the specified one. (#59)
    • Merged a documentation update from @ataylor32 regarding the new method of generating access tokens. (#58)
  • Made a number of cosmetic improvements to AptFs, my FUSE-based filesystem that provides a view on unpacked Debian source packages as regular folders.
  • Updated the SSL certificate for try.diffoscope.org, a hosted version of the diffoscope in-depth and content-aware diff utility. Continued thanks to Bytemark for sponsoring the hardware.

Debian & Reproducible builds

Whilst anyone can inspect the source code of free software for malicious flaws, most GNU/Linux distributions provide binary (or "compiled") packages to end users. The motivation behind the Reproducible Builds effort is to allow verification that no flaws have been introduced — either maliciously and accidentally — during this compilation process by promising identical binary packages are always generated from a given source.

  • Presented a talk entitled "Reproducible Builds" talk at Software Freedom Kosova, in Prishtina, Republic of Kosovo.

  • I filed my 2,500th bug in the Debian BTS: #840972: golang-google-appengine: accesses the internet during build.

  • In order to build packages reproducibly, one not only needs identical sources but also some external and sharable definition of the environment used for a particular build, stipulating such things such as the version numbers of the required build-dependencies.

    It is not currently clear how to handle these .buildinfo files after the archive software has processed them and how to make them available to the world so I started development on a proof-of-concept server to see what issues arise in practice. It is available at buildinfo.debian.net.

  • Chaired an IRC meeting and ran a poll to determine a regular time .

  • Submitted two design proposals to our wiki page.

  • Improvements to our tests.reproducible-builds.org testing framework:

    • Move regular "Scheduled in..." messages to the #debian-reproducible-changes IRC channel.
    • Use our log_info method instead of manual echo calls.
    • Correct an "all sources packages" → "all source packages" typo.
    • Submit .buildinfo files to buildinfo.debian.net.
    • Create GPG key on nodes for buildinfo.debian.net at deploy time, not "lazily".

My work in the Reproducible Builds project was also covered in our weekly reports. (#75, #76, #77 & #78).


I also submitted 14 patches to fix specific reproducibility issues in bio-eagle, cf-python, fastx-toolkit, fpga-icestorm, http-icons, lambda-align, mypy, playitslowly, seabios, stumpwm, sympa, tj3, wims-help & xotcl.

Debian LTS

This month I have been paid to work 13 hours on Debian Long Term Support (LTS). In that time I did the following:

  • Seven days of "frontdesk" duties, triaging CVEs, etc.
  • Issued DLA 647-1 for freeimage correcting an out-of-bounds write vulnerability in the XMP image handling functionality.
  • Issued DLA 649-1 for python-django fixing a possible CSRF protection bypass on sites that use Google Analytics.
  • Issued DLA 654-1 for libxfixes preventing an integer overflow when a malicious client sent INT_MAX as a "length".
  • Issued DLA 662-1 for quagga correcting a programming error where two constants were confused that could cause stack overrun in IPv6 routing code.
  • Issued DLA 688-1 for cairo to prevent a DoS attack where a malicious SVG could generate invalid pointers.
Patches contributed
Uploads
  • gunicorn:
    • 19.6.0-7 — Set supplementary groups when changing uid, add an example systemd .service file to gunicorn-examples, and expand README.Debian to make it clearer what to do now that /etc/gunicorn.d has been removed.
    • 19.6.0-8 — Correct previous supplementary groups patch to be compatible with Python 3.
  • redis:
    • 3:3.2.4-2 — Ensure that sentinel's configuration actually writes to a pidfile location so that systemd can detect that the daemon has started.
    • 3:3.2.5-1 — New upstream release.
  • libfiu:
    • 0.94-8 — Fix FTBFS under Bash due to lack of && in debian/rules.
    • 0.94-9 — Ensure the build is reproducible by sorting injected modules.
  • aptfs (2:0.8-2) — Minor cosmetic changes.

Sponsored uploads
NMUs
  • libxml-dumper-perl (0.81-1.2) — Move away from a unsupported debhelper compat level 4.
  • netatalk (2.2.5-1.1) — Drop build-dependency on hardening-includes.

QA uploads
  • anon-proxy (00.05.38+20081230-4) — Move to a supported debhelper compatibility level 9.
  • ara (1.0.32) — Make the build reproducible.
  • binutils-m68hc1x (1:2.18-8) — Make the build reproducible & move to a supported debhelper compatibility level.
  • fracplanet (0.4.0-5) — Make the build reproducible.
  • libnss-ldap (265-5) — Make the build reproducible.
  • python-uniconvertor (1.1.5-3) — Fix an "option release requires an argument" FTBFS. (#839375)
  • ripole (0.2.0+20081101.0215-3) — Actually include the ripole binary in package. (#839919) & enable hardening flags.
  • twitter-bootstrap (2.0.2+dfsg-10) — Fix incorrect copyright formatting when building under Bash. (#824592)
  • zpaq (1.10-3) — Make the build reproducible.
Bugs filed (without patches)

I additionally filed 7 bugs for packages that access the internet during build against berkshelf, golang-google-appengine, node-redis, python-eventlet, python-keystoneclient, python-senlinclient & tornado-pyvows.


RC bugs

I also filed 65 FTBFS bugs against android-platform-external-jsilver, auto-multiple-choice, awscli, batmon.app, bgpdump, cacti-spine, cucumber, check, debci, eximdoc4, freetennis, freezegun, gatos, git/gnuit, gnucash, grads, haskell-debian, haskell-hsopenssl-x509-system, homesick, ice-builder-gradle, kscreen, latex-cjk-japanese-wadalab, libdbd-firebird-perl, libgit2, libp11, libzypp, mozart-stdlib, mqtt-client, mtasc, musicbrainzngs, network-manager-openvpn, network-manager-vpnc, nim, node-lodash, node-once, npgsql, ocamlbuild, ocamldsort, ohai, partclone, plaso, polyglot-maven, projectreactor, python-launchpadlib, python-pygraphviz, python-pygraphviz, python-pygraphviz, python-textile, qbittorrent, qbrew, qconf, qjoypad, rdp-alignment, reel, ruby-foreman, ruby-gettext, ruby-gruff, ruby-rspec-rails, samtools, sbsigntool, spock, sugar, taglib-extras, tornado-pyvows, unifdef, virt-top, vmware-nsx & zshdb.

Debian FTP Team

As a Debian FTP assistant I ACCEPTed 147 packages: ace-link, amazon-s2n, avy, basez, bootstrap-vz, bucklespring, camitk, carettah, cf-python, debian-reference, dfcgen-gtk, efivar, entropybroker, fakesleep, gall, game-data-packager, gitano, glare, gnome-panel, gnome-shell-extension-dashtodock, gnome-shell-extension-refreshwifi, gnome-shell-extension-remove-dropdown-arrows, golang-github-gogits-go-gogs-client, golang-github-gucumber-gucumber, golang-github-hlandau-buildinfo, golang-github-hlandau-dexlogconfig, golang-github-hlandau-goutils, golang-github-influxdata-toml, golang-github-jacobsa-crypto, golang-github-kjk-lzma, golang-github-miekg-dns, golang-github-minio-sha256-simd, golang-github-nfnt-resize, golang-github-nicksnyder-go-i18n, golang-github-pointlander-compress, golang-github-pointlander-jetset, golang-github-pointlander-peg, golang-github-rfjakob-eme, golang-github-thecreeper-go-notify, golang-github-twstrike-gotk3adapter, golang-github-unknwon-goconfig, golang-gopkg-dancannon-gorethink.v1, golang-petname, haskell-argon2, haskell-binary-parsers, haskell-bindings-dsl, haskell-deriving-compat, haskell-hackage-security, haskell-hcwiid, haskell-hsopenssl-x509-system, haskell-megaparsec, haskell-mono-traversable-instances, haskell-prim-uniq, haskell-raaz, haskell-readable, haskell-readline, haskell-relational-record, haskell-safe-exceptions, haskell-servant-client, haskell-token-bucket, haskell-zxcvbn-c, irclog2html, ironic-ui, lace, ledger, libdancer2-plugin-passphrase-perl, libdatetime-calendar-julian-perl, libdbix-class-optimisticlocking-perl, libdbix-class-schema-config-perl, libgeo-constants-perl, libgeo-ellipsoids-perl, libgeo-functions-perl, libgeo-inverse-perl, libio-async-loop-mojo-perl, libmojolicious-plugin-assetpack-perl, libmojolicious-plugin-renderfile-perl, libparams-validationcompiler-perl, libspecio-perl, libtest-time-perl, libtest2-plugin-nowarnings-perl, linux, lua-scrypt, mono, mutt-vc-query, neutron, node-ansi-font, node-buffer-equal, node-defaults, node-formatio, node-fs-exists-sync, node-fs.realpath, node-is-buffer, node-jison-lex, node-jju, node-jsonstream, node-kind-of, node-lex-parser, node-lolex, node-loud-rejection, node-random-bytes, node-randombytes, node-regex-not, node-repeat-string, node-samsam, node-set-value, node-source-map-support, node-spdx-correct, node-static-extend, node-test, node-to-object-path, node-type-check, node-typescript, node-unset-value, nutsqlite, opencv, openssl1.0, panoramisk, perl6, pg-rage-terminator, pg8000, plv8, puppet-module-oslo, pymoc, pyramid-jinja2, python-bitbucket-api, python-ceilometermiddleware, python-configshell-fb, python-ewmh, python-gimmik, python-jsbeautifier, python-opcua, python-pyldap, python-s3transfer, python-testing.common.database, python-testing.mysqld, python-testing.postgresql, python-wheezy.template, qspeakers, r-cran-nleqslv, recommonmark, rolo, shim, swift-im, tendermint-go-clist, tongue, uftrace & zaqar-ui.

Categories: LUG Community Blogs

Debian Bits: "softWaves" will be the default theme for Debian 9

Planet HantsLUG - Tue, 25/10/2016 - 17:50

The theme "softWaves" by Juliette Taka Belin has been selected as default theme for Debian 9 'stretch'.

After the Debian Desktop Team made the call for proposing themes, a total of twelve choices have been submitted, and any Debian contributor has received the opportunity to vote on them in a survey. We received 3,479 responses ranking the different choices, and softWaves has been the winner among them.

We'd like to thank all the designers that have participated providing nice wallpapers and artwork for Debian 9, and encourage everybody interested in this area of Debian, to join the Design Team. It is being considered to package all of them so they are easily available in Debian. If you want to help in this effort, or package any other artwork (for example, particularly designed to be accessibility-friendly), please contact the Debian Desktop Team, but hurry up, because the freeze for new packages in the next release of Debian starts on January 5th, 2017.

This is the second time that Debian ships a theme by Juliette Belin, who also created the theme "Lines" that enhances our actual stable release, Debian 8. Congratulations, Juliette, and thank you very much for your continued commitment to Debian!

Categories: LUG Community Blogs

Chris Lamb: Concorde

Planet ALUG - Mon, 24/10/2016 - 18:59

Today marks the 13th anniversary since the last passenger flight from New York arrived in the UK. Every seat was filled, a feat that had become increasingly rare for a plane that was a technological marvel but a commercial flop….


  • Only 20 aircraft were ever built despite 100 orders, most of them cancelled in the early 1970s.
  • Taxiing to the runway consumed 2 tons of fuel.
  • The white colour scheme was specified to reduce the outer temperature by about 10°C.
  • In a promotional deal with Pepsi, F-BTSD was temporarily painted blue. Due to the change of colour, Air France were advised to remain at Mach 2 for no more than 20 minutes at a time.
  • At supersonic speed the fuselage would heat up and expand by as much as 30cm. The most obvious manifestation of this was a gap that opened up on the flight deck between the flight engineer's console and the bulkhead. On some aircraft conducting a retiring supersonic flight, the flight engineers placed their caps in this expanded gap, permanently wedging the cap as it shrank again.
  • At Concorde's altitude a breach of cabin integrity would result in a loss of pressure so severe that passengers would quickly suffer from hypoxia despite application of emergency oxygen. Concorde was thus built with smaller windows to reduce the rate of loss in such a breach.
  • The high cruising altitude meant passengers received almost twice the amount of radiation as a conventional long-haul flight. To prevent excessive exposure, the flight deck comprised of a radiometer; if the radiation level became too high, pilots would descend below 45,000 feet.
  • BA's service had a greater number of passengers who booked a flight and then failed to appear than any other aircraft in their fleet.
  • Market research later in Concorde's life revealed that customers thought Concorde was more expensive than it actually was. Ticket prices were progressively raised to match these perceptions.
  • The fastest transatlantic airliner flight was from New York JFK to London Heathrow on 7 February 1996 by British Airways' G-BOAD in 2 hours, 52 minutes, 59 seconds from takeoff to touchdown. It was aided by a 175 mph tailwind.


See also: A Rocket to Nowhere.

Categories: LUG Community Blogs

Mick Morgan: do not click here

Planet ALUG - Mon, 24/10/2016 - 11:22

I have just noticed that the getsafeonline campaign’s website contains this wonderfully ironic side bar graphic.

Go on, you know you want to.

Categories: LUG Community Blogs

Daniel Silverstone (Kinnison): Gitano - Approaching Release - Deprecated commands

Planet ALUG - Mon, 24/10/2016 - 02:24

As mentioned previously I am working toward getting Gitano into Stretch. Last time we spoke about lace, on which a colleague and friend of mine (Richard Maw) did a large pile of work. This time I'm going to discuss deprecation approaches and building more capability out of fewer features.

First, a little background -- Gitano is written in Lua which is a deliberately small language whose authors spend more time thinking about what they can remove from the language spec than they do what they could add in. I first came to Lua in the 3.2 days, a little before 4.0 came out. (The authors provide a lovely timeline in case you're interested.) With each of the releases of Lua which came after 3.2, I was struck with how the authors looked to take a number of features which the language had, and collapse them into more generic, more powerful, smaller, fewer features.

This approach to design stuck with me over the subsequent decade, and when I began Gitano I tried to have the smallest number of core features/behaviours, from which could grow the power and complexity I desired. Gitano is, at its core, a set of files in a single format (clod) stored in a consistent manner (Git) which mediate access to a resource (Git repositories). Some of those files result in emergent properties such as the concept of the 'owner' of a repository (though that can simply be considered the value of the project.owner property for the repository). Indeed the concept of the owner of a repository is a fiction generated by the ACL system with a very small amount of collusion from the core of Gitano. Yet until recently Gitano had a first class command set-owner which would alter that one configuration value.

[gitano] set-description ---- Set the repo's short description (Takes a repo) [gitano] set-head ---- Set the repo's HEAD symbolic reference (Takes a repo) [gitano] set-owner ---- Sets the owner of a repository (Takes a repo)

Those of you with Gitano installations may see the above if you ask it for help. Yet you'll also likely see:

[gitano] config ---- View and change configuration for a repository (Takes a repo)

The config command gives you access to the repository configuration file (which, yes, you could access over git instead, but the config command can be delegated in a more fine-grained fashion without having to write hooks). Given the config command has all the functionality of the three specific set-* commands shown above, it was time to remove the specific commands.

Migrating

If you had automation which used the set-description, set-head, or set-owner commands then you will want to switch to the config command before you migrate your server to the current or any future version of Gitano.

In brief, where you had:

ssh git@gitserver set-FOO repo something

You now need:

ssh git@gitserver config repo set project.FOO something

It looks a little more wordy but it is consistent with the other features that are keyed from the project configuration, such as:

ssh git@gitserver config repo set cgitrc.section Fooble Section Name

And, of course, you can see what configuration is present with:

ssh git@gitserver config repo show

Or look at a specific value with:

ssh git@gitserver config repo show specific.key

As always, you can get more detailed (if somewhat cryptic) help with:

ssh git@gitserver help config

Next time I'll try and touch on the new PGP/GPG integration support.

Categories: LUG Community Blogs

Mick Morgan: NFC? NFW

Planet ALUG - Sat, 22/10/2016 - 19:48

As is our custom on a Saturday, this morning my wife and I went out to a local cafe for breakfast. We know the proprietress so I was chatting to her whilst paying for the meal. Part way through the chat, the cafe proprietress tore off the receipt from the POS terminal and removed my debit card and handed it back to me.

Me: “Hang on, I haven’t entered my PIN. Are you sure that has been paid?”
CP: “Yes, it says here it’s paid.”
Me: “I have NOT authorised that transaction. It cannot be paid.”
CP: “Oh, don’t worry, we accept “swipe to pay” it probably just authorised that as you put your card in the terminal.”
Me: “That cannot happen. That card is not “swipe to pay” enabled. And I haven’t authorised any payment yet.”
CP (looking at receipt): “It says here “Contactless Sale” and the payment has been authorised”.
Me: “Show me that receipt.”

Sure enough, the receipt showed a “Contactless Sale” for the amount of the breakfast, however, the card type shown, and the last four digits of the card quoted were not those of my debit card. But I did recognise the card type as one I hold in my wallet so I checked that. Sure enough, that card has the WiFi symbol on it and the last four digits matched that on the Cafe receipt. So the POS terminal had taken the payment from a card in my wallet and not the card I had actually inserted.

That should not happen. And the fact that it did worries the hell out of me.

At the time the payment was taken, my wallet holding the other card was in my left hand (I had just removed my debit card from it with my right hand because I am right handed). So I placed that wallet on the counter beside me so that I could pick up the POS terminal in my left hand allowing me push my debit card in with my right hand and then enter my PIN. Replaying that action afterwards I am absolutely certain that at no time was my wallet anywhere nearer than a foot or more away from the POS terminal. Moreover that terminal had a card inserted – my debit card – and it should have been waiting for my PIN authorisation. So what happened?

I don’t know. And as I said above, that worries me.

I have checked both Wikipedia for details of the standards used in passive NFC of the type used in contactless payment and the “Security FAQ” for contactless payments on the Smart Card Alliance site (warning, PDF). Both those references tell me what I thought I already knew – NFC is only supposed to work at ranges of up to 2-4 inches (or 10 cm). No way was my wallet ever anywhere near 10 cm from that POS terminal. The closest it could have been was at least a foot away.

If this can happen to me, then I am certain it must have happened to others. Possibly to others who have been charged for someone else’s transaction simply because their NFC enabled card happens to be within range of the POS in question. In such cases, neither the actual customer nor the unwitting person really charged for the transaction would be any the wiser at the time of the transaction. Nor would the retailer know or care because they have a receipt for a contactless sale.

I’ll bet there have been some interesting conversations between such unwitting payers and their banks when the payment was noticed and then disputed.

Meanwhile, I’m going to find out whether I can get a card without the NFC capability to replace the card I unwittingly used to pay for breakfast. No way do I want this to happen again.

Categories: LUG Community Blogs

Mick Morgan: variable substitution in lighttpd

Planet ALUG - Wed, 19/10/2016 - 16:25

I’ve been a lighty user for many years now, having junked apache when it became obviously overweight for my target devices (the slugs in particular). Trivia is, of course, powered by lighty as are all my other websites.

Lighty’s configuration file syntax is reasonably simple to understand, and is well documented on the Redmine wiki. The guys at Calomel.org have also put together quite a nice introduction to lighty. If you haven’t tried it, and find that apache is becoming too much of a resource hog for you, I’d recommend that you give lighty a run.

I use lighty’s access control mechanisms to prevent random bots and bad guys from reaching trivia’s administrative functions and I do this in much the same way as I limit access to my ssh and openvpn daemons – I restrict access to the fixed IP address assigned to my router by my ISP. So in the lighty virtual host configuration file I use the following construct:

$HTTP[“remoteip”] !~ “12.34.56.78” {
$HTTP[“url”] =~ “^/wp-admin/” {
url.access-deny = (“”)
}

That says: if the remote IP address is not 12.34.56.78, then deny access to the wp-admin directory.

Now I have several virtual hosts running and I also protect several directories. I also use a similar construct to redirect all my own access to my websites to port 443 so that I can always be certain that my own connection is encrypted and my authentication credentials will be protected. This means, of course, that I have several entries of the form: “if this IP address, then take this action” dotted around my configuration files. Not good. A recent change of ISP meant that my IP address has changed and I needed to edit my configuration files or find myself locked out. The most important files to change were my iptables rules so that I could still get ssh access on all my VMs. This didn’t take long because I have all the important configuration details (ssh IP addresses and ports, openvpn port, DNS addresses etc.) defined at the head of the bash script. One change is all that is necessary and bash variable substitution takes care of the rest. But my lighty configuration files were a different matter and I had to check carefully to ensure that I didn’t miss an important change. That’s just daft. Surely lighty allows for variable assignment and substitution. And of course it does, I just hadn’t checked before now.

The syntax looks like this:

At the head of the configuration file make an entry of the form:

# set our fixed remote ip address used in access control

IP = “23.45.67.89”

and then change the earlier configuration lines to:

$HTTP[“remoteip”] !~ IP {
$HTTP[“url”] =~ “^/wp-admin/” {
url.access-deny = (“”)
}

Simple, and I feel a complete idiot for not noticing this before.

Categories: LUG Community Blogs
Syndicate content