Verzenden vanaf een forward e-mailadres

Ik heb een e-mailadres dat geforward wordt naar mijn “normaal” e-mailadres. Nu was het onlangs nodig om een bericht vanaf dat adres te versturen. Helaas vond mijn mailserver dat geen goed idee, en weigerde hij pertinent.

De oplossing was simpel. In de client moet je de forward instellen als Van (i.e. pieter@example.org), en op de server moet je het adres toevoegen aan smtpd_sender_login_maps in /etc/postfix/main.cf (in mijn geval in een apart bestand):

pieter@example.org pieter@mail.helptux.be

Nog even postmappen (enkel wanneer het een apart bestand is), server herstarten en klaar.

Error 400 on SERVER: Could not find class “foo” for “node” on “node”

Alweer een Puppetprobleem. Opeens zegt de puppet-agent dat hij een bepaalde module niet kan vinden op de master. Maar, zoals in alle slechte verhalen, de module was er wel. En ze stond op de juiste plaats.

Zowat alles werd onderzocht. De module stond er, was correct, vertoonde geen syntaxfouten; de server had rechten om te lezen en uit te voeren; kortom, er was geen enkele reden waarom het niet zou werken.

En toch

Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Could not find class

Frustrerend.

Na nog wat extra google-fu werd de oplossing gevonden in een ondergesneeuwd antwoord op deze vraag. Was de metadata.json wel correct? En jawel. Ik had de parameters “requirements” en “dependencies” omgedraaid. requirements verwijst naar de verwachte puppetversie en is niet verplicht. De puppetmodules die nodig zijn voor de module staan in dependencies. En helaas bleek ik die te hebben omgewisseld.

Op zich geen probleem (afhankelijkheden worden niet gecontroleerd in onze setup), maar ik had enkel “requirements” en niet “dependencies“. En dependencies is verplicht, en als een verplichte parameter ontbreekt, dan weigert puppet dienst. Met een wel heel verwarrende foutmelding tot gevolg.

PXE-boot voor Ubuntu 16.04 op Ubuntu 14.04

Jaren geleden (2014) heb ik een tweede- of derdehands Dell rackmount-server gekocht voor wanneer “dat eens van pas zou komen”. En dat moment is nu gekomen; het wordt een monitoringserver voor de lokale radio, om te kunnen zien of een bepaald cruciaal onderdeel is uitgevallen vooraleer de radio off-air gaat.

Maar dan moet er natuurlijk een OS op. En zo’n server kan enkel PXE-booten, maar dat kan mijn netwerk dan weer niet zomaar. Dus, om een lang verhaal kort te maken, enkele aanpassingen drongen zich op: de thuis-manusje-van-alles-server moet ook nog eens PXE en TFTP ondersteunen, terwijl de router dat moet kunnen delegeren naar de server.

Netwerk

Te beginnen met het netwerk: de router is een Mikrotik met RouterOS v6.41. En die kan dat. Maar niet op de geijkte manier (met DHCP-options 66 en 67), maar wel zo:

/ip dhcp-server network
add address=192.168.0.0/24 boot-file-name=pxelinux.0 comment=dhcp-lan dhcp-option="" dns-server=192.168.0.1 gateway=192.168.0.1 netmask=25 next-server=192.168.0.200 ntp-server=192.168.0.200

(waarbij 192.168.0.200 het adres is van de server met PXE/TFTP)

Server

En dan nu de server. Op het systeem (serapeum) staat Ubuntu 14.04 LTS, maar uiteraard wil ik Ubuntu 16.04 LTS (de volgende LTS) (eigenlijk wou ik CentOS 7, maar dat is een beetje te veel van het goede).

Vooraleer te beginnen, open de firewall op poort 69 voor udp-verkeer en maak een map aan om de installatiebestanden op te slaan (bijvoorbeeld /srv/tftp).

Installeer dan een paar packages:

apt-get install tftpd-hpa inetutils-inetd syslinux

Geen dhcp-server of nfs-server; DHCP is iets voor de router en NFS gebruiken we niet.

Inetd zal gebruikt worden om de tftp-service te starten, dus moeten er aanpassingen gebeuren in /etc/initetd.conf:

tftp dgram udp wait root /usr/sbin/in.tftpd /usr/sbin/in.tftpd -s /srv/tftp

Volgende werk is de configuratie van tftp (in dit geval tftpd-hpa) zelf. In /etc/default/tftpd-hpa:

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"
RUN_DAEMON="yes"
OPTIONS="-l -s /srv/tftp"

/srv/tftp is de map die in het begin is aangemaakt om de installatiebestanden te bevatten. De rest zijn min of meer de standaardwaarden.

Uiteraard moet de service nog ingeschakeld worden en moet ook starten bij het opstarten, voor het gemak.

update-initd --enable BOOT
service tftpd-hpa start

De voorlaatste stap is de configuratie van de bootloader. We (of beter, tftp en PXE) gebruiken hiervoor Syslinux.

Begin met in srv/tftp de map pxelinux.cfg aan te maken, en daarna vesamenu.c32 en pxelinux.0 uit /usr/lib/syslinux naar /srv/tftp te kopiëren. De bestanden uit /usr/lib/syslinux zijn nodig voor de bootloader, terwijl in pxelinux.cfg de configuratie voor de bootloader komt.

Het configuratiebestand van de bootloader heet, zeer creatief, default en bevindt zich in pxelinux.cfg. Het is niet zo ingewikkeld; maar een beetje “klassiek”. Meer informatie is te vinden op de wiki van syslinux, dat dient als bootloader voor PXE.

DEFAULT vesamenu.c32
TIMEOUT 100
PROMPT 0
MENU INCLUDE pxelinux.cfg/PXE.conf
NOESCAPE 1
LABEL Install Ubuntu 16.04 Server
MENU LABEL Install Ubuntu 16.04 Server
kernel Ubuntu/16.04/amd64/linux
append initrd=Ubuntu/16.04/amd64/initrd.gz vga=788
ENDTEXT

Ook ik ben niet zo creatief geweest. Wat tussen LABEL en ENDTEXT staat zijn de menu-entries. Ik heb er maar één, Install Ubuntu 16.04 Server. kernel verwijst naar de Linux-kernel; append voegt parameters toe aan de kernel. initrd verwijst naar de initial ramdisk en vga naar de vga-modus. initrd en kernel zijn bestanden in Ubuntu/16.04/amd64 en zijn gekopieerd van de Ubuntu 16.04 netinstall-images.

Als je meer items in het menu wil kan dat door extra LABELENDTEXT-blokken toe te voegen.

Naast default moet er in pxelinux.cfg ook nog pxe.conf staan, waar we in default ook naar verwijzen (MENU INCLUDE pxelinux.cfg/PXE.conf). Het moge duidelijk zijn dat die entry niet case-sensitive is.

pxe.conf bevat niet veel interessants:

MENU TITLE PXE Server
NOESCAPE 1
ALLOWOPTIONS 1
PROMPT 0
MENU WIDTH 80
MENU ROWS 14
MENU TABMSGROW 24
MENU MARGIN 10
MENU COLOR border 30;44 #ffffffff #00000000 std

Volgende (en laatste) stap is het kopiëren van de kernel en initrd van de minimal-installation-image van Ubuntu 16.04. Download de ISO, mount hem en kopieer linux en initrd.gz vanuit de root van de CD naar de installatiemappen. Zorg ervoor dat de locatie dezelfde is als die in pxelinux.cfg/default, anders verwijst dat bestand naar iets wat niet bestaat. En dan zal het niet werken.

Finale

Dat was het eigenlijk. Als alles goed geïnstalleerd en geconfigureerd is, de firewall open staat en de computer waar je iets op wil installeren PXE-booting ondersteunt, dan zou het moeten werken. Veel plezier!

(Een echte systeembeheerder zou natuurlijk Kickstart en/of preseed configureren naast PXE-boot, maar ik ben niet meteen van plan om een installatiefarm op te zetten.)

mod_rewrite, Let’s Encrypt en een vervelend probleem

Ik ben een fervent aanhanger van Let’s Encrypt, en van het versleutelen van verkeer in het algemeen. Al mijn publieke en private sites hebben dus een certificaat, dat bij voorkeur automatisch vernieuwd wordt.

Maar al een tijdje is er een probleem met één van die sites (een Mattermostserver achter een Apache reverse proxy), waarbij het hernieuwen van het certificaat (via cron) om de één of andere reden faalt. Op zich was er met DNS en de configuratie van de webserver niets mis: die was identiek aan alle andere systemen.

Het enige verschil was dat er een groot aantal RewriteRule‘s geconfigureerd stonden, met daarin een aantal catch-all-regels. Nu is het zo dat ik altijd een RewriteRule toevoeg voor Let’s Encrypt, om alle aanvragen voor de acme-challenge-token op een centrale plaats te bewaren:

RewriteRule ^/\.well-known/acme-challenge/(.*)$ /var/letsencrypt/.well-known/acme-challenge/$1

Door een te snelle lezing van de documentatie was ik vergeten dat Apache alle regels die matchen uitvoert, niet alleen de eerste. Hierdoor werden alle aanvragen doorgestuurd naar de achterliggende Mattermostserver, die, terecht, antwoordde met 403 Forbidden. En dan werkt Let’s Encrypt natuurlijk niet.

De oplossing staat gelukkig ook in de documentatie:  de [L]-flag zorgt ervoor dat de huidige regel meteen ook de laatste regel is die wordt geïnterpreteerd.

RewriteRule ^/\.well-known/acme-challenge/(.*)$ /var/letsencrypt/.well-known/acme-challenge/$1 [L]

Et voilà, alle requests van Let’s Encrypt worden meteen correct doorgestuurd, en de andere regels worden zelfs niet meer bekeken. En ik heb mijn certificaten terug!

Environment variables in wsgi-applicaties

Soms heb je een environment variable nodig in een wsgi-applicatie, zoals bijvoorbeeld LD_LIBRARY_PATH omdat een bepaalde C-bibliotheek niet beschikbaar is.

De normale manier om dat te doen is via mod_env en SetEnv in de VirtualHost-definitie, maar voor wsgi-applicaties werkt dat niet, omdat die gestart worden voor de eerste request. SetEnv wordt enkel uitgevoerd bij de eerste request naar een site.

Je moet die variables meegeven aan het apache (httpd)-proces zelf, met dien verstande dat ze dan voor alle websites en webapplicaties beschikbaar zijn (wat misschien niet de bedoeling is). Op Ubuntu-gebaseerde systemen moet dat in /etc/apache2/envvars, op RedHat-gebaseerde systemen gebruiken /etc/sysconfig/httpd:

LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/opt/apa/lib"

Een procesherstart later zou de variable moeten beschikbaar zijn.

ObjectId & PyMongo

Het is een beetje weggestopt in de documentatie, maar het staat er gelukkig wel in. Wanneer je een document uit een mongodatabase wil halen op basis van zijn _id, dan moet je dat id eerst omzetten naar een ObjectId, anders krijg je onbegrijpelijke “Document not found“-foutmeldingen.

Alweer een uur gespaard!

Puppetmodules in RubyMine

In RubyMine, een uitstekende IDE uit de Jetbrainsstal, kan je Puppetmodules ontwikkelen (de taal is ondersteund), maar je moet enkele dingen in het achterhoofd houden.

Zo eentje heb ik vandaag ontdekt. Opdat RubyMine automatisch de naam van variabelen, functies en klassen uit jouw module zou aanvullen, moet je ervoor zorgen dat ze op één of andere manier door heeft dat je een module aan het ontwikkelen bent.

De oplossing is eenvoudig en elegant: voeg een metadata.json-bestand toe en zorg ervoor dat de structuur van de module overeen komt met de documentatie.

Een kleine moeite, een wereld van verschil.