fogbound.net




Fri, 9 Jul 2021

Virtual Apache SSL Hosts in a Docker Container

— SjG @ 1:40 pm

You might want to “containerize” your development hosting environment so you can easily migrate it from machine to machine. As a Docker noob, I had a bunch of issues getting this set up the first time, and wanted to share a working configuration. This example assumes you have Docker installed and operating. You can also skip reading this, and just download the files at GitHub.

First, we’ll need to create some directories. I create one for the Apache configurations, and one for the code projects I’ll be working on.
mkdir apache-php
mkdir project

Within project, you can check out the code for your various projects into subdirectories. For simplicity, I’ve created project1 and project2 in the sample code. The Apache web server within the container will serve content from these directories.

We’re going to use a fictional top-level domain (TLD) for our development environment. This way, the URL you use to access your sites will be the same every time you spin up a new dev environment, without having to worry about name servers. You do, however, have to worry about your /etc/hosts file (or your platform’s equivalent). Choose a TLD that will be easy to remember. For my example, I’m using “mylocal”. One thing to avoid: start the TLD with a letter rather than a number. Don’t include characters like hyphens. Please learn from my mistakes.

Edit your /etc/hosts, and add the lines:
127.0.0.1 project1.mylocal
127.0.0.1 project2.mylocal

Next, we’ll want to create an SSL certificate for use in development. The easiest way to do this is with mkcert. Once you have mkcert installed and working, you’ll create a wildcard certificate for your TLD:
cd php-apache
mkcert -install mylocal "*.mylocal"

Next, we create a compose.yaml file:

version: "3.9"
services:
php:
container_name: ApachePHPVirtual
networks:
- apache
build:
context: .
dockerfile: PhpApacheDockerfile
volumes:
- "./project:/var/www"
ports:
- 80:80
- 443:443
extra_hosts:
- "project1.mylocal:127.0.0.1" # remember to add "127.0.0.1 project1.mylocal" to your /etc/hosts file or equivalent
- "project2.mylocal:127.0.0.1" # remember to add "127.0.0.1 project2.mylocal" to your /etc/hosts file or equivalent
hostname: project1.mylocal # default
domainname: mylocal
tty: true # if you want to debug
networks:
apache:

This is pretty straightforward. We’re creating a container which we’ll call “ApachePHPVirtual,” it will have a network we call “apache” if we want to connect using other containers, and it links our top level project directory to /var/www in the container. We map ports 80 and 443 on our host machine to those same ports in the container. The extra_hosts directive adds our project names to the container’s /etc/hosts. We set up the container’s hostname to match our first project, and set the default domain to our “mylocal” TLD.

We then want to create configurations for each of the Apache virtual hosts. In the php-apache directory, we create config files for each project. These are just standard virtual host declarations, e.g.:

<VirtualHost *:80>
    ServerName project1.mylocal
    Redirect permanent / https://project1.mylocal/
</VirtualHost>
<VirtualHost *:443>
    ServerName project1.mylocal
    DocumentRoot /var/www/project1
    ErrorLog ${APACHE_LOG_DIR}/project1-error.log
    CustomLog ${APACHE_LOG_DIR}/project1-access.log combined
    DirectoryIndex index.php

    <Directory "/var/www/project1">
        Options -Indexes +FollowSymLinks
        AllowOverride all
        Order allow,deny
        Allow from all
    </Directory>

    SSLEngine On
    SSLCertificateFile    /etc/apache2/ssl/cert.pem
    SSLCertificateKeyFile /etc/apache2/ssl/cert-key.pem
</VirtualHost>

You’ll need to create a similar configuration for each project. Note that the Document Root points at the mapped host directory. That means you won’t need to rebuild the container to see project changes.

The actual image for the Apache/PHP container is created and configured in our next file, “PhpApacheDockerfile”. So we create that:

FROM php:8.0.8-apache-buster

# add some packages
RUN docker-php-ext-install curl gd iconv pdo pdo_mysql soap zip

# Apache Config
COPY php-apache/project1.conf /etc/apache2/sites-available/project1.conf
COPY php-apache/project2.conf /etc/apache2/sites-available/project2.conf
COPY php-apache/mylocal+1-key.pem /etc/apache2/ssl/cert-key.pem
COPY php-apache/mylocal+1.pem /etc/apache2/ssl/cert.pem

# mod rewrite! SSL!
RUN a2enmod rewrite
RUN a2enmod ssl

# enable sites
RUN a2ensite project1.conf
RUN a2ensite project2.conf
RUN service apache2 restart

This pulls the php-8.0.8 image from DockerHub, adds in some PHP extensions, copies over our SSL certificate and key, copies our virtual host configuration files over, enables the projects, and restarts the Apache server.

Now, all that remains to do is build it and power it up:

docker-compose build && docker-compose up -d

You can now visit the project URls in your browser, e.g., https://project1.mylocal/


Sat, 12 Sep 2020

Those pesky /usr/local/include headers under MacOS Catalina

— SjG @ 11:04 am

Here’s another of those “system upgrade moves stuff around” problems. My work iMac seems to be suffering a slow disk failure. It gets slower and slower as it tries to run.

Queue restoring TimeMachine backups onto a new iMac. A lot of stuff just works. But things like building xsendfile for the Apache development server under Mac ports threw lots of errors. The compiler couldn’t find the headers:

mod_xsendfile.c fatal error: stdio.h: no such file or directory

There are lots of suggestions out there to run xcode-select --install, but I’d already done that.

Turns out that XCode has stopped storing the SDKs in /Library/Developer/CommandLineTools/SDKs and moved them to /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs. I spent a bit of time trying to figure out how to pass that path to the Mac Ports version of apxs2 to use the new include path, but eventualy gave up and just did the hacky thing:
ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs /Library/Developer/CommandLineTools/SDKs

Another issue, just so I rememebr next time, was that the web server and the command line were mysteriously running different versions of PHP. The key, of course, was that I had forgotten to run port select, e.g., for this project, I needed port select php php70.


Tue, 12 May 2020

Remembering things for my future forgetful self: developer’s edition

— SjG @ 3:57 pm

I need to record a bunch of training tutorials for a web site. I want to use consistent, fake data, so I set up a local copy of the site. I dumped out the database, and created my test data. As the site evolves, I can continue to check out revisions and run migrations, but still have my customized test data.

So far, so good. But a problem arises. When recording, the URL for my demo site shows up. This will confuse the customer. There’s an easy fix for this, of course, which is to change my demo site to respond to the true site’s hostname, and map it in my /etc/hosts file.

It works great! Except that I’m forgetful. I’ll be adding a new feature on my dev machine, get it approved on the staging server, and eventually check it out of revision control to launch it to the live server. I’ll check, and get confused. Where are my changes? I may go so far as to start looking at the filesystem of the live server and issuing informational commands in the revision control system, which will only confuse me more, as the changes are live. Eventually, I’ll notice that some of the data is demo-like data, and then I’ll kick myself for having wasted half an hour on something so stupid. I’ll comment out the line in my /etc/hosts, and everything will be back to normal… until it comes time to record new tutorials.

It’s time to remind future me. First step is in the terminal. I added the following to my .zshrc file:

export inhost=`egrep '127\.0\.0\.1\s+domain.com' /etc/hosts | wc -l`
export commented=`egrep '#127\.0\.0\.1\s+domain.com' /etc/hosts | wc -l`
if [[ "$inhost" -eq 1 && "$commented" -eq 0 ]]
then
    echo " ____  _______        ___    ____  _____ _"
    echo "| __ )| ____\ \      / / \  |  _ \| ____| |"
    echo "|  _ \|  _|  \ \ /\ / / _ \ | |_) |  _| | | "
    echo "| |_) | |___  \ V  V / ___ \|  _ <| |___|_|"
    echo "|____/|_____|  \_/\_/_/   \_\_| \_\_____(_)"
    echo ""
    echo "-----------------------------------------"
    echo "domain.com is remapped in /etc/hosts!!! "
    echo "-----------------------------------------"
fi

This works well. When I’ve edited the /etc/hosts, my terminal comes up like:

Terminal with warning

But, future me may not go directly to the terminal to diagnose the issue. Future me may foolishly try to compare the current state of the site to the dev or staging version. Never fear! I can save future me from this idiocy.

First, I set my standard test browser to open up new windows and tabs with a “home page” instead of a blank page. That “home page” is http://localhost. I set up the default virtual host, and create an index page with the following barebones code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Home</title>
</head>
<body>
<?php
$server = dns_get_record('domain.com');
if ($server[0]['ip'] == '127.0.0.1')
{ ?>
  <h1 style="color:red">WARNING!</h1>
  <pre style="color:red">
-----------------------------------------
domain.com is remapped in /etc/hosts!!!
-----------------------------------------
  </pre>
  <?php } else { ?> we good.<?php } ?>
  <h2>Search:</h2>
  <form class="search-form-home__form" action="https://startpage.com/sp/search" id="search" method="post">
    <div>
       <input id="q" maxlength="2048" name="query" type="text" value=""/>
       <input type="hidden" name="cat" value="web"/>
       <input type="submit"/>
    </div>
  </form>
</body>
</html>

Now when future me opens up a new browser tab or window, he’ll be greeted with a nearly blank page with a search box. Or possibly a warning!

Warning in browser!

So, by spending a bunch of time now, I can save future me a bunch of time (and frustration). It’s not clear that it will be worth it, but it was definitely worth trying.


Fri, 27 Mar 2020

Fixing obsolete Karma test framework

— SjG @ 3:20 pm

Somewhere along the line, I did some software update on my MacBook machine that broke the Karma tests for an AngularJS 1.x application. I don’t know when this happened. I haven’t done work on this project on any computer except on my work desktop in a long, long time.

However, with Covid-19 and the work-from-home regimen, I need to make this work on the MacBook.

When I’d run the tests, I’d get a lovely crash:

27 03 2020 14:35:38.395:ERROR [reporter]: Can not load reporter "junit", it is not registered!
   Perhaps you are missing some plugin?
 27 03 2020 14:35:38.473:INFO [karma]: Karma v2.0.5 server started at http://0.0.0.0:9876/
 27 03 2020 14:35:38.474:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
 27 03 2020 14:35:38.475:ERROR [karma]: { inspect: [Function: inspect] }

Naturally, the first thing I’d do is make sure I’d installed the correct plugin: npm install karma-junit-reporter --save … Node happily reinstalled the plugin, and nothing changed. The source of the issue turned out to be a conflict between the project-installed karma-cli and bits and bobs of an old global install.

I had to clean up the globally-installed stuff that was lurking in /usr/local/bin and /usr/local/lib/node_modules (which is to say, deleted /usr/local/lib/node_modules/karma, /usr/local/lib/node_modules/karma-jasmine, and /usr/local/lib/node_modules/jasmine) leaving everything in the local project install with the exception of karma-cli, which is still globally installed.


Wed, 2 Jan 2019

Using Makefiles for Teensy Development on Mac OS

— SjG @ 9:24 am

Even if you don’t want to use the Arduino IDE for building your application, or want to do fancy things that only the evil, convoluted syntax of Makefiles can provide, you can do Teensy development on the Mac from the command line. While there are a lot of guides out there on how to do it, I hadn’t seen a complete step-by-step set of instructions.

So with no further ado, here are the steps for building Teensy applications on Mac OS using the Makefiles and the mighty command line.

  1. Download the Arduino IDE from https://www.arduino.cc/en/main/software and install it.
  2. Download Teensyduino from https://www.pjrc.com/teensy/td_download.html and install it.
  3. Download a “basic project template” for Teensy. The original is at https://github.com/apmorton/teensy-template but I’ve been using the fork at https://github.com/a-j-f/teensy-template which has been updated more recently and uses the Teensy CLI Loader for uploading to the device.
  4. Update the Makefile to set your Teensy model
  5. Type make to build, or make upload to install on your Teensy

If you’re wanting to monitor the serial console of your Teensy over USB, install a terminal program like CoolTerm. Alternatively, if you use Homebrew or MacPorts you can use a console-based terminal program like minicom. Once the USB connector is plugged in, your Teensy will show up as something like /dev/cu.usbmodem45504901. The communications settings will be 115200 baud, 8 bit, no parity, 1 stop bit.