Alpine Pi – dnsmasq, lighttpd & squid (DNS, web server & proxy server)

I’m still ‘working out the kinks’ in my Alpine based DMZ server. I’ve got DNS via dnsmasq running (well), lighttpd web server running (seemingly well), and squid (proxy to web) running (OK, with https config issue).

This is the core set of services needed for general web browsing / ad blocking / efficient pipe (telco) bandwidth use.

Before we start, there are a couple of things I find annoying about the ‘busybox’ on the Alpine. While it gives you ‘most’ of the common Linux tools, some of them are more limited. All ‘livable’, but a couple of my work habits need modification. Searching for text inside the ‘vi’ editor is dodgy, for example. A staple in my life… Similarly, using !! for “do the last command again” is missing in ‘ash’ (the default shell). God I use that a lot. Nothing horrid, but not a ‘Daily Driver” desktop. Probably a security feature on a DMZ box as there is less to attack.

The DNS services let me quash sites that just spy on me or send trash to me. It can also let you block entire domains. For example, you could send *.cn to the localhost address ( ) and prevent any name that resolves to a China high level domain name from getting contacted.

The web server, as of now, just lets me put out a simple “Hi!” if anyone contacts it. This is useful (at least in theory, I’m still working on the practice) as a responder to those DNS re-routes of web requests. So instead of letting send you a MB of crap on your “by the byte WiFi” link, you can instead have your local web server respond with “Hi!”. (Many folks set up a 1 transparent pixel reply, so you never see anything. I’m going to do that… just after I get the “Hi!” response to show…). Why do this? Well, waiting for timeouts and error messages is not as fast as a local Ethernet speed response. Essentially, it is better to have a landing space for those website redirects via the DNS entries.

Then there is squid. I’ve covered it before when I installed it on Debian. The purpose is mostly to just cache frequently used web pages and images so you only need to download them once. Saves wire time and cost. Saves you waiting at the keyboard while the same old same old crap downloads the 1000th time today…

There are also incidental security benefits from all of these. DNS blocking of evil actors is clearly a win. Avoiding “whatever” is in those bits of website crap via your own server is a win if any of them are malware. Squid is less of a protector, but it does still protect some. Attacks against “you” via web page ports will tend to land on the squid server instead. It can be made much more heavily protected. (That is, you can remove software and commands that could be used by an attacker, it can run a hardened kernel, you don’t have your personal information on it, etc. etc.)

That’s the “why” and the “what” of where I’ve gotten so far.


The DNS server, dnsmasq, is working great. DNS lookups are faster, and many sites are blocked. Essentially it is a full win, with little in the way of ‘grief’ from it. The “open issues” mainly are that I’ve got a fixed list of domain names to block, and that ought to be an automated created list. (Or, have a fixed list and add an automated update list…) I just point it to my Telco router as the first bring up stage, so it is just a filter and buffer in front of the regular DNS chosen by my Telco ISP. After that’s working, then I change the list of DNS servers to put an ‘ad blocking’ one first, then the Telco boundary router (as it is sitting on my desk and I think it, too, may cache a bit), and after that some alternatives in case of DDoS attacks. How many and which ones to list is personal choice. I tend to avoid the Google DNS servers (that everyone else suggests to use) simply because of their information harvesting behaviour. I don’t need Google tracking every DNS lookup from my IP address to be used in DNC targeting. (Yes, Google was ‘caught’ offering services to the DNC to profile every voter in the country…)

The lighttpd server seems to also be working fine. When I connect to the server, it responds with “Hi!”. What is not working is to get “Hi!” when a 404 page missing error is triggered (or perhaps some other error). Using Opera on the Tablet I get a long message about how Opera is sorry but it can’t fetch that page… so some more work needed. (Other browsers just silently display nothing, but that is still taking a timeout lag instead of a “Hi!” near instant service…) But basic web server function is clearly up and working well.

Then there is squid. It works FINE for http: requests. It refuses connections for https: requests IF I activate the cache service. By default, https: requests are just ‘passed through’ without any cache services. (This lets you have more complete end to end encryption and privacy, but with the loss of cache services). If left that way, it works fine. Turn on the cache services (that need a ‘man-in-the-middle’ encrypted tunnel TO the server and a second tunnel server To Website)) and it denies the connect. “Has issues” and it is likely my configuration not being tuned right.

I was going to wait until I got the wrinkles ironed out, but then got nothing done over the weekend. OK, so that means I’m going to air the dirty configs and add caveats. (A minor punishment for sloth…) Just realize these are NOT finished polished just dandy configs. They need more done to reach ‘finished and polished’. But they can get you started.

DNS via dnsmasq

First off, realize that a squid server has some DNS (Domain Name Service) server abilities too. DNS being critical to a lot of things, and folks wanting ‘control’, and DNS historically having been a PITA and “often poor”, it now gets diddled with all over the place. The latest example of this is that SystemD wants to take over DNS too… Part of why I like doing this on Alpine is I don’t have to second guess what SystemD is doing to thwart my wishes.

The dnsmasq service is fairly trivial to set up. At first, it does look a fright, but it isn’t. Mostly you can just ignore the boiler plate. As usual, you install it with [local package manager name] [add|install|whatever] dnsmasq and configure a configuration file. For Alpine, the package manager is ‘apk’ and the command is ‘add’.

dnspi:/home/chiefio# apk add dnsmasq
OK: 42 MiB in 69 packages

The config file is in /etc:

dnspi:/home/chiefio# cd /etc
dnspi:/etc# ls -l dnsmasq.conf 
-rwxr-xr-x    1 root     root         27443 Nov  7 22:32 dnsmasq.conf
dnspi:/etc# wc -l dnsmasq.conf 
687 dnsmasq.conf

Notice that it is 687 lines long. That’s why I said you can ignore most of it. 90% ish of it is comments and things commented out as examples you might need for some bizarre case. A few lines matter (as is usually the case for simple home systems). Those lines starting with a “#” are comments and don’t do anything.

Here’s the whole thing, but I’m changing some of the IP numbers to protect from too much information leakage… I’m also going to point out the lines I changed with BOLD and comments.

dnspi:/etc# cat dnsmasq.conf 
# Configuration file for dnsmasq.
# Format is one option per line, legal options are the same
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.

# Listen on this specific port instead of the standard DNS port
# (53). Setting this to zero completely disables DNS function,
# leaving only DHCP and/or TFTP.

# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# unnecessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link unnecessarily.

# Never forward plain names (without a dot or domain part)
# Never forward addresses in the non-routed address spaces.

# Uncomment these to enable DNSSEC validation and caching:
# (Requires dnsmasq to be built with DNSSEC option.)

# Replies which are not DNSSEC signed may be legitimate, because the domain
# is unsigned, or may be forgeries. Setting this option tells dnsmasq to
# check that an unsigned reply is OK, by finding a secure proof that a DS 
# record somewhere between the root and the domain does not exist. 
# The cost of setting this is that even queries in unsigned domains will need
# one or more extra DNS queries to verify.

# Uncomment this to filter useless windows-originated DNS requests
# which can trigger dial-on-demand links needlessly.
# Note that (amongst other things) this blocks all SRV requests,
# so don't use it if you use eg Kerberos, SIP, XMMP or Google-talk.
# This option only affects forwarding, SRV records originating for
# dnsmasq (via srv-host= lines) are not suppressed by it.

# Change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf

# By  default,  dnsmasq  will  send queries to any of the upstream
# servers it knows about and tries to favour servers to are  known
# to  be  up.  Uncommenting this forces dnsmasq to try each query
# with  each  server  strictly  in  the  order  they   appear   in
# /etc/resolv.conf

# If you don't want dnsmasq to read /etc/resolv.conf or any other
# file, getting its servers from this file instead (see below), then
# uncomment this.

# If you don't want dnsmasq to poll /etc/resolv.conf or other resolv
# files for changes and re-read them then uncomment this.

# Add other name servers here, with domain specs if they are for
# non-public domains.
#nameserver   (ad-blocking-dns2)
#nameserver     (Telco router)
#nameserver  (Open DNS)
#nameserver     (
#nameserver     (
#nameserver         (
#nameserver         (
#nameserver         (

Note that I have comments listing a bunch of known DNS servers. When some DDos (Distributed Denial of Service) attack is taking down your regular servers, it is nice to have a list handy, and what could be more handy than the place you will be going to change your server settings?

I also do ‘strick order’ so it looks to itself (and cache) first, then the ad.block.server, and then goes on to try others only if needed.

# Example of routing PTR queries to nameservers: this will send all
# address->name queries for 192.168.3/24 to nameserver

# Add local-only domains here, queries in these domains are answered
# from /etc/hosts or DHCP only.

# Add domains which you want to force to an IP address here.
# The example below send any host in to a local
# web-server.


I put a couple here as a proof of concept. Most are in an external file.

I also don’t do any IPv6. Any IoT device using IPv6 on my network will get nowhere.

# --address (and --server) work with IPv6 addresses too.

# Add the IPs of all queries to,, and their
# subdomains to the vpn and search ipsets:

# You can control how dnsmasq talks to a server: this forces
# queries to to be routed via eth1
# server=

# and this sets the source (ie local) address used to talk to
# to port 55 (there must be a interface with that
# IP on the machine, obviously).
# server=

# If you want dnsmasq to change uid and gid to something other
# than the default, edit the following lines.

# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
# Or you can specify which interface _not_ to listen on
# Or which to listen on by address (remember to include if
# you use this.)
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP and TFTP on it.

# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.

# If you don't want dnsmasq to read /etc/hosts, uncomment the
# following line.
# or if you want it to read another file, as well as /etc/hosts, use
# this.

# Set this (and domain: see below) if you want to have a domain
# automatically added to simple names in a hosts-file.

# Set the domain for dnsmasq. this is optional, but if it is set, it
# does the following things.
# 1) Allows DHCP hosts to have fully qualified domain names, as long
#     as the domain part matches this setting.
# 2) Sets the "domain" DHCP option thereby potentially setting the
#    domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"

# Set a different domain for a particular subnet,

# Same idea, but range rather then subnet,,

# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.

# This is an example of a DHCP range where the netmask is given. This
# is needed for networks we reach the dnsmasq DHCP server via a relay
# agent. If you don't know what a DHCP relay agent is, you probably
# don't need to worry about this.

# This is an example of a DHCP range which sets a tag, so that
# some DHCP options may be set only for this network.

# Use this DHCP range only when the tag "green" is set.

# Specify a subnet which can't be used for dynamic address allocation,
# is available for hosts with matching --dhcp-host lines. Note that
# dhcp-host declarations will be ignored unless there is a dhcp-range
# of some type for the subnet in question.
# In this case the netmask is implied (it comes from the network
# configuration on the machine running dnsmasq) it is possible to give
# an explicit netmask instead.

# Enable DHCPv6. Note that the prefix-length does not need to be specified
# and defaults to 64 if missing/
#dhcp-range=1234::2, 1234::500, 64, 12h

# Do Router Advertisements, BUT NOT DHCP for this subnet.
#dhcp-range=1234::, ra-only 

# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack 
# hosts. Use the DHCPv4 lease to derive the name, network segment and 
# MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC alogrithm.
#dhcp-range=1234::, ra-names

# Do Router Advertisements, BUT NOT DHCP for this subnet.
# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
#dhcp-range=1234::, ra-only, 48h

# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
# so that clients can use SLAAC addresses as well as DHCP ones.
#dhcp-range=1234::2, 1234::500, slaac

# Do Router Advertisements and stateless DHCP for this subnet. Clients will
# not get addresses from DHCP, but they will get other configuration information.
# They will use SLAAC for addresses.
#dhcp-range=1234::, ra-stateless

# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
# from DHCPv4 leases.
#dhcp-range=1234::, ra-stateless, ra-names

# Do router advertisements for all subnets where we're doing DHCPv6
# Unless overriden by ra-stateless, ra-names, et al, the router 
# advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the 
# clients don't use SLAAC addresses.

# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
# need to be on the same network. The order of the parameters in these
# do not matter, it's permissible to give name, address and MAC in any
# order.

# Always allocate the host with Ethernet address 11:22:33:44:55:66
# The IP address

# Always set the name of the host with hardware address
# 11:22:33:44:55:66 to be "fred"

# Always give the host with Ethernet address 11:22:33:44:55:66
# the name fred and IP address and lease time 45 minutes

# Give a host with Ethernet address 11:22:33:44:55:66 or
# 12:34:56:78:90:12 the IP address Dnsmasq will assume
# that these two Ethernet interfaces will never be in use at the same
# time, and give the IP address to the second, even if it is already
# in use by the first. Useful for laptops with wired and wireless
# addresses.

# Give the machine which says its name is "bert" IP address
# and an infinite lease

# Always give the host with client identifier 01:02:02:04
# the IP address

# Always give the Infiniband interface with hardware address
# 80:00:00:48:fe:80:00:00:00:00:00:00:f4:52:14:03:00:28:05:81 the
# ip address The client id is derived from the prefix
# ff:00:00:00:00:00:02:00:00:02:c9:00 and the last 8 pairs of
# hex digits of the hardware address.

# Always give the host with client identifier "marjorie"
# the IP address

# Enable the address given for "judge" in /etc/hosts
# to be given to a machine presenting the name "judge" when
# it asks for a DHCP lease.

# Never offer DHCP service to a machine whose Ethernet
# address is 11:22:33:44:55:66

# Ignore any client-id presented by the machine with Ethernet
# address 11:22:33:44:55:66. This is useful to prevent a machine
# being treated differently when running under different OS's or
# between PXE boot and OS boot.

# Send extra options which are tagged as "red" to
# the machine with Ethernet address 11:22:33:44:55:66

# Send extra options which are tagged as "red" to
# any machine with Ethernet address starting 11:22:33:

# Give a fixed IPv6 address and name to client with 
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also the they [] around the IPv6 address are obilgatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5] 

# Ignore any clients which are not specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unknown-clients".
# This relies on the special "known" tag which is set when
# a host is matched.

# Send extra options which are tagged as "red" to any machine whose
# DHCP vendorclass string includes the substring "Linux"

# Send extra options which are tagged as "red" to any machine one
# of whose DHCP userclass strings includes the substring "accounts"

# Send extra options which are tagged as "red" to any machine whose
# MAC address matches the pattern.

# If this line is uncommented, dnsmasq will read /etc/ethers and act
# on the ethernet-address/IP pairs found there just as if they had
# been given as --dhcp-host options. Useful if you keep
# MAC-address/host mappings there for other purposes.

# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Common options can be given to dnsmasq by name:
# run "dnsmasq --help dhcp" to get a list.
# Note that all the common settings, such as netmask and
# broadcast address, DNS server and default route, are given
# sane defaults by dnsmasq. You very likely will not need
# any dhcp-options. If you use Windows clients and Samba, there
# are some options which are recommended, they are detailed at the
# end of this section.

# Override the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.

# Do the same thing, but using the option name

# Override the default route supplied by dnsmasq and send no default
# route at all. Note that this only works for the options sent by
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# for all other option numbers.

# Set the NTP time server addresses to and

# Send DHCPv6 option. Note [] around IPv6 addresses.

# Send DHCPv6 option for namservers as the machine running 
# dnsmasq and another.

# Ask client to poll for option changes every six hours. (RFC4242)

# Set option 58 client renewal time (T1). Defaults to half of the
# lease time if not specified. (RFC2132)

# Set option 59 rebinding time (T2). Defaults to 7/8 of the
# lease time if not specified. (RFC2132)

# Set the NTP time server address to be the same machine as
# is running dnsmasq

# Set the NIS domain name to "welly"

# Set the default time-to-live to 50

# Set the "all subnets are local" flag

# Send the etherboot magic flag and then etherboot options (a string).

# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
# Note that the tag: part must precede the option: part.
#dhcp-option = tag:red, option:ntp-server,

# The following DHCP options set up dnsmasq in the same way as is specified
# for the ISC dhcpcd in
# adapted for a typical dnsmasq installation where the host running
# dnsmasq is also the host running samba.
# you may want to uncomment some or all of them if you use
# Windows clients and Samba.
#dhcp-option=19,0           # option ip-forwarding off
#dhcp-option=44,     # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
#dhcp-option=45,     # netbios datagram distribution server
#dhcp-option=46,8           # netbios node type

# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.

# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this......

# Send RFC-3442 classless static routes (note the netmask encoding)

# Send vendor-class specific options encapsulated in DHCP option 43.
# The meaning of the options is defined by the vendor-class so
# options are sent only when the client supplied vendor class
# matches the class given here. (A substring match is OK, so "MSFT"
# matches "MSFT" and "MSFT 5.0"). This example sets the
# mtftp address to for PXEClients.

# Send microsoft-specific option to tell windows to release the DHCP lease
# when it shuts down. Note the "i" flag, to tell dnsmasq to send the
# value as a four-byte integer - that's what microsoft wants. See

# Send the Encapsulated-vendor-class ID needed by some configurations of
# Etherboot to allow is to recognise the DHCP server.

# Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here.
# See for details.
# Magic number - needed before anything else is recognised
# Configuration file name
# Path prefix
# Reboot time. (Note 'i' to send 32-bit value)

# Set the boot filename for netboot/PXE. You will only need
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# external one. (See below for how to enable the TFTP server.)

# The same as above, but use custom tftp-server instead machine running dnsmasq

# Boot for Etherboot gPXE. The idea is to send two different
# filenames, the first loads gPXE, and the second tells gPXE what to
# load. The dhcp-match sets the gpxe tag for requests from gPXE.
#dhcp-match=set:gpxe,175 # gPXE sends a 175 option.

# Encapsulated options for Etherboot gPXE. All the options are
# encapsulated within option 175
#dhcp-option=encap:175, 1, 5b         # priority code
#dhcp-option=encap:175, 176, 1b       # no-proxydhcp
#dhcp-option=encap:175, 177, string   # bus-id
#dhcp-option=encap:175, 189, 1b       # BIOS drive code
#dhcp-option=encap:175, 190, user     # iSCSI username
#dhcp-option=encap:175, 191, pass     # iSCSI password

# Test for the architecture of a netboot client. PXE clients are
# supposed to send their architecture as option 93. (See RFC 4578)
#dhcp-match=peecees, option:client-arch, 0 #x86-32
#dhcp-match=itanics, option:client-arch, 2 #IA64
#dhcp-match=hammers, option:client-arch, 6 #x86-64
#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64

# Do real PXE, rather than just booting a single file, this is an
# alternative to dhcp-boot.
#pxe-prompt="What system shall I netboot?"
# or with timeout before first available action is taken:
#pxe-prompt="Press F8 for menu.", 60

# Available boot services. for PXE.
#pxe-service=x86PC, "Boot from local disk"

# Loads /pxelinux.0 from dnsmasq TFTP server.
#pxe-service=x86PC, "Install Linux", pxelinux

# Loads /pxelinux.0 from TFTP server at
# Beware this fails on old PXE ROMS.
#pxe-service=x86PC, "Install Linux", pxelinux,

# Use bootserver on network, found my multicast or broadcast.
#pxe-service=x86PC, "Install windows from RIS server", 1

# Use bootserver at a known IP address.
#pxe-service=x86PC, "Install windows from RIS server", 1,

# If you have multicast-FTP available,
# information for that can be passed in a similar way using options 1
# to 5. See page 19 of

# Enable dnsmasq's built-in TFTP server

# Set the root directory for files available via FTP.

# Do not abort if the tftp-root is unavailable

# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.

# This option stops dnsmasq from negotiating a larger blocksize for TFTP
# transfers. It will slow things down, but may rescue some broken TFTP
# clients.

# Set the boot file name only when the "red" tag is set.

# An example of dhcp-boot with an external TFTP server: the name and IP
# address of the server are given after the filename.
# Can fail with old PXE ROMS. Overridden by --pxe-service.

# If there are multiple external tftp servers having a same name
# (using /etc/hosts) then that name can be specified as the
# tftp_servername (the third option to dhcp-boot) and in that
# case dnsmasq resolves this name and returns the resultant IP
# addresses in round robin fasion. This facility can be used to
# load balance the tftp load among a set of servers.

# Set the limit on DHCP leases, the default is 150

# The DHCP server needs somewhere on disk to keep its lease database.
# This defaults to a sane location, but if you want to change it, use
# the line below.

# Set the DHCP server to authoritative mode. In this mode it will barge in
# and take over the lease for any client which broadcasts on the network,
# whether it has a record of the lease or not. This avoids long timeouts
# when a machine wakes up on a new network. DO NOT enable this if there's
# the slightest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information:

# Run an executable when a DHCP lease is created or destroyed.
# The arguments sent to the script are "add" or "del",
# then the MAC address, the IP address and finally the hostname
# if there is one.

# Set the cachesize here.
# Note this is a VERY big cache size, 10 x the default

# If you want to disable negative caching, uncomment this.

# Normally responses which come from /etc/hosts and the DHCP lease
# file have Time-To-Live set as zero, which conventionally means
# do not cache further. If you are happy to trade lower load on the
# server for potentially stale date, you can set a time-to-live (in
# seconds) here.

# If you want dnsmasq to detect attempts by Verisign to send queries
# to unregistered .com and .net hosts to its sitefinder service and
# have dnsmasq instead return the correct NXDOMAIN response, uncomment
# this line. You can add similar lines to do the same for other
# registries which have implemented wildcard A records.

# If you want to fix up DNS results from upstream servers, use the
# alias option. This only works for IPv4.
# This alias makes a result of appear as
# and this maps 1.2.3.x to 5.6.7.x
# and this maps> to>

# Change these lines if you want dnsmasq to serve MX records.

# Return an MX record named "" with target
# and preference 50,,50

# Set the default target for MX records created using the localmx option.

# Return an MX record pointing to the mx-target for all local
# machines.

# Return an MX record pointing to itself for all local machines.

# Change the following lines if you want dnsmasq to serve SRV
# records.  These are useful if you want to serve ldap requests for
# Active Directory and other windows-originated DNS requests.
# See RFC 2782.
# You may add multiple srv-host lines.
# The fields are ,,,,
# If the domain part if missing from the name (so that is just has the
# service and protocol sections) then the domain given by the domain=
# config option is used. (Note that expand-hosts does not need to be
# set for this to work.)

# A SRV record sending LDAP for the domain to
# port 389,,389

# A SRV record sending LDAP for the domain to
# port 389 (using domain=)

# Two SRV records for LDAP, each with different priorities,,389,1,,389,2

# A SRV record indicating that there is no LDAP server for the domain

# The following line shows how to make dnsmasq serve an arbitrary PTR
# record. This is useful for DNS-SD. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for PTR records.)
#ptr-record=_http._tcp.dns-sd-services,"New Employee Page._http._tcp.dns-sd-services"

# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for TXT records.)

#Example SPF.,"v=spf1 a -all"

#Example zeroconf,name=value,paper=A4

# Provide an alias for a "local" DNS name. Note that this _only_ works
# for targets which are names from DHCP or /etc/hosts. Give host
# "bert" another name, bertrand

# For debugging purposes, log each DNS query as it passes through
# dnsmasq.

# Log lots of extra information about DHCP transactions.

# Include another lot of configuration options.

# Include all the files in a directory except those ending in .bak

# Include all files in a directory which end in .conf

Here is a bit of my /etc/hosts.block file. It is 15,655 lines long, so can’t post all of it ;-). I’ve got a couple of sources glued into it, but the top is from one of them and has were to get their part:

dnspi:/etc/squid# head /etc/hosts.block
# This MVPS HOSTS file is a free download from:            #
#                  #
[...]                                                     #
# [Misc A - Z]

Where “” would be the address of your web server inside your shop, or would be “” if you have it running on your personal workstation and want to land there. (That is the default as shipped from the maker).

Web Server – lighttpd

Works well, added with:

apk add lighttpd

Configuration done here:

dnspi:/etc# ls -l /etc/lighttpd/
total 32
-rw-r--r--    1 root     root            27 Nov 13 02:05 error-handler.html
-rw-r--r--    1 root     root          8241 Nov 13 02:27 lighttpd.conf
-rw-r--r--    1 root     root          3436 Jun  3 16:33 mime-types.conf
-rw-r--r--    1 root     root           869 Jun  3 16:33 mod_cgi.conf
-rw-r--r--    1 root     root           683 Jun  3 16:33 mod_fastcgi.conf
-rw-r--r--    1 root     root           488 Jun  3 16:33 mod_fastcgi_fpm.conf

Note that only ‘error-handler.html’ and ‘lighttpd.conf’ have changed to the install date. Even there, the ‘error-handler.html’ file is a failed attempt to get “Hi!” on errors. The only thing that really is changed is the lighttpd.conf file. I spent a ‘long time’ looking for where error-handler.html was stored. Eventualy I found that there’s a path name setting that goes on in the conf file. Since it is “user configurable” it isn’t in the web pages… even though just about everyone will take the default.

Here’s parts of my (very limited and maybe not quite right) conf file:

dnspi:/etc/lighttpd# wc -l lighttpd.conf 
323 lighttpd.conf

dnspi:/etc/lighttpd# cat lighttpd.conf 
# Default lighttpd.conf for Gentoo.
# $Header: /var/cvsroot/gentoo-x86/www-servers/lighttpd/files/conf/lighttpd.conf,v 1.3 2005/09/01 14:22:35 ka0ttic Exp $

# {{{ variables
var.basedir  = "/var/www/localhost"
var.logdir   = "/var/log/lighttpd"
var.statedir = "/var/lib/lighttpd"
# }}}

# {{{ modules
# At the very least, mod_access and mod_accesslog should be enabled.
# All other modules should only be loaded if necessary.
# NOTE: the order of modules is important.

So 323 lines in it, most of them boilerplate or comments. I note in passing that it is the “Gentoo” default… Gee, I wonder what base release is used by the Alpine folks for their build? ;-)

Note that this is where “things are located” is defined. Log files in /var/log/lighttpd with state information in /var/lib/lighttpd and most important, you put your web pages in /var/www/localhost. Except you don’t… Next is a long section of “modules” all of which are commented out except for the two they tell you to use. Then follows a lot of things to do, or not do, where I just accepted all the defaults. Note this one, though:

# {{{ server settings
server.username      = "lighttpd"
server.groupname     = "lighttpd"

server.document-root = var.basedir + "/htdocs"      = "/run/"

server.errorlog      = var.logdir  + "/error.log"
# log errors to syslog instead
#   server.errorlog-use-syslog = "enable"

Notice that line “server.document-root”? that tags “/httdocs” onto the path you thought you were using from the top…

dnspi:/etc/lighttpd# ls -l /var/www/localhost/htdocs/
total 12
-rw-r--r--    1 root     root            27 Nov 13 02:03 error-handler.html
-rw-r--r--    1 root     root             4 Nov  7 22:27 index.html
-rw-r--r--    1 root     root             4 Nov 13 02:28 status-404.html

The “index.html” says “Hi!” and works on connection to the server. The other two are attempts to get the missing page handled… that are not working yet. It just doesn’t respond.

On this error handler one I tried, and failed, to just get a simple web page displayed.

# error-handler for status 404
server.error-handler-404 = "/index.html"
# server.error-handler-404 = "/error-handler.php"

No, I have no idea why it didn’t work. Hopefully it isn’t tied to type .php files…

It then goes through a lot of server cache settings that I left at default along with some other setup for services I’m not using like veritual machines.

FWIW, here’s the entire file if you remove all the comments and blank lines:

dnspi:/etc/lighttpd# grep -v "^#" lighttpd.conf 

var.basedir  = "/var/www/localhost"
var.logdir   = "/var/log/lighttpd"
var.statedir = "/var/lib/lighttpd"
server.modules = (
include "mime-types.conf"
server.username      = "lighttpd"
server.groupname     = "lighttpd"
server.document-root = var.basedir + "/htdocs"      = "/run/"
server.errorlog      = var.logdir  + "/error.log"
server.indexfiles    = ("index.php", "index.html",
						"index.htm", "default.htm")
server.follow-symlink = "enable"
server.error-handler-404 = "/index.html"
server.errorfile-prefix    = var.basedir + "/error/status-"
static-file.exclude-extensions = (".php", ".pl", ".cgi", ".fcgi")
accesslog.filename   = var.logdir + "/access.log"
url.access-deny = ("~", ".inc")

Pretty small active set. You would think I’d have figured it out by now…

The squid proxy server

Yup, you guessed it, installed with:

apk add squid

Configured in /etc/squid:

dnspi:/etc# ls -l squid
total 360
-rw-r--r--    1 root     root           692 Nov  4 12:59 cachemgr.conf
-rw-r--r--    1 root     root           692 Nov  4 12:59 cachemgr.conf.default
-rw-r--r--    1 root     root          1817 Nov  4 12:58 errorpage.css
-rw-r--r--    1 root     root          1817 Nov  4 12:59 errorpage.css.default
-rw-r--r--    1 root     root         12077 Nov  4 12:59 mime.conf
-rw-r--r--    1 root     root         12077 Nov  4 12:59 mime.conf.default
-rw-r--r--    1 root     root          5069 Nov  7 22:40 squid.conf
-rw-r--r--    1 root     root          4233 Nov  7 18:42 squid.conf.NoSSL
-rw-r--r--    1 root     root          2315 Nov  4 12:59 squid.conf.default
-rw-r--r--    1 root     root        289464 Nov  4 12:59 squid.conf.documented
-rw-r--r--    1 root     root          5089 Nov  7 18:48 squid.conf.withSSL
-r--------    1 root     root          5435 Nov  7 18:37 squid.pem

Note that I’ve cot a ‘mime.conf.default’ that is a vendor supplied backup of the installed one, and a ‘squid.conf.NoSSL’ along with a ‘squid.conf.default’ and a ‘squid.conf.withSLL’. These are my attempts to make https: work with caching and saving copies of the ‘without’ that works. The squid.pem file is my private key for the https: encryption process (there’s a procedure to create that). FWIW, the bulk of what makes my squid.conf so much bigger than the squid.conf.default is a higher level of comments. I copied it from my prior squid server on Debian and they were more wordy.

As this posting is already long, I’m not going to put my entire squid config here. (For one thing, WordPress spell checking has an exponential slowdown with posting length and on the Pi M3 it reaches a ‘too slow to live with’ about this long. For another, it isn’t working right yet…) Here’s the “with SSL” with all the comments and spaces stripped out (most of it the defaults):

dnspi:/etc/squid# grep -v "^#" squid.conf.withSSL 
acl localnet src	# RFC1918 possible internal network
acl localnet src	# RFC1918 possible internal network
acl localnet src	# RFC1918 possible internal network
acl SSL_ports port 443
acl Safe_ports port 80		# http
acl Safe_ports port 21		# ftp
acl Safe_ports port 443		# https
acl Safe_ports port 70		# gopher
acl Safe_ports port 210		# waiss
acl Safe_ports port 1025-65535	# unregistered ports
acl Safe_ports port 280		# http-mgmt
acl Safe_ports port 488		# gss-http
acl Safe_ports port 591		# filemaker
acl Safe_ports port 777		# multiling http
acl QUERY urlpath_regex cgi-bin \? asp aspx jsp
cache deny QUERY
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access deny to_localhost
http_access allow localnet
http_access allow localhost
http_access deny all
http_port 4353
cache_dir aufs /var/cache/squid 1024 16 256
access_log none
cache_mem 256 MB
maximum_object_size 10 MB
read_ahead_gap 64 KB
forwarded_for delete 
httpd_suppress_version_string on
shutdown_lifetime 30 seconds
request_header_access User-Agent deny all
request_header_replace User-Agent Mozilla/5.0 (Windows; MSIE 9.0; Windows NT 9.0; en-US)
visible_hostname The_Shadow 
refresh_pattern ^ftp:		1440	20%	10080
refresh_pattern ^gopher:	1440	0%	1440
refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
refresh_pattern .		0	20%	4320
always_direct allow all
ssl_bump server-first all
sslproxy_cert_error allow all
sslproxy_flags DONT_VERIFY_PEER
http_port 4353 ssl-bump cert=/etc/squid/squid.pem key=/etc/squid/squid.pem generate-host-certificates=on options=NO_SSLv2

I did set cache dir and made max objects and memory use larger, perhaps they need to be the defauls on the pi… I made my testing Host Name “The Shadow” just for grins and chose to have it present a Mozilla ‘user agent’ since I don’t really want the “mobile” pages just ’cause I’m on an ARM chip.

Well, for what it’s worth, that’s what I’ve got so far.

In Conclusion

Hopefully that’s helpful to someone and it can save you searching web pages for a few hours trying to find out where lighttpd puts web page html files…

It isn’t hard to install any of these and get them basically running. It’s the niggly little things like making https: pages cache too that takes time and head scratching.

With that, Back to work for me…

Subscribe to feed


About E.M.Smith

A technical managerial sort interested in things from Stonehenge to computer science. My present "hot buttons' are the mythology of Climate Change and ancient metrology; but things change...
This entry was posted in Tech Bits and tagged , , , , , , . Bookmark the permalink.

7 Responses to Alpine Pi – dnsmasq, lighttpd & squid (DNS, web server & proxy server)

  1. E.M.Smith says:

    Two notes to self:

    1) It looks like the /etc/hosts.block file is not doing anything. Sucking the list into the /etc/hosts file causes the names to resolve to the lighttpd web server address.

    2) Squid wasn’t giving me the local server since it decided to only look at /etc/resolv.conf unless you explicitly tell it otherwise with a ‘directive’ that isn’t listed in the model config… Sigh. Semi-hidden special behaviour…

    Squid configuration directive dns_nameservers

    Available in: 4 3.5 3.4 3.3 3.2 2.7 3.1 3.0 2.6

    For older versions than 3.3 see the linked pages above
    Configuration Details:
    Option Name: dns_nameservers
    Default Value: Use operating system definitions
    Suggested Config:

    Use this if you want to specify a list of DNS name servers
    (IP addresses) to use instead of those given in your
    /etc/resolv.conf file.

    On Windows platforms, if no value is specified here or in
    the /etc/resolv.conf file, the list of DNS name servers are
    taken from the Windows registry, both static and dynamic DHCP
    configurations are supported.

    Example: dns_nameservers

    OK, now I’ve got the whole list being blocked (but I’m wondering just why it isn’t working to point dnsmasq to an outside added file… maybe the . in the name is being ‘special’…?) and I’ve figured out why squid wasn’t using the blocking dns server (as I’d not set /etc/resolv.conf to point anywhere but the DHCP default on boot and figured dnsmasq was all that mattered, it being a dns server and all… silly me…)

    OK, one open issue is ‘will /etc/resolv.conf be overwritten at reboot’? since many things can fight over it. If so, then I need to not just change it, but add that dns_nameservers directive to squid.conf file.

    This is the kind of stuff that adds days to a 2 hour project… figuring out where there’s a default set to point at something, but not ‘documented’ in the config file with a commented example. You get to have things fail, then “go fish”, then read a manual page… or figuring out what documented ‘feature’ is not actually working… and why.

    Well, at least now when I point my browser at “” it puts up the “Hi!” page from my server! Now I just need to figure out why Opera on the Tablet is still showing ‘error page not found’ instead of the “Hi!”… Not exactly a priority, but…

  2. E.M.Smith says:

    I added this to my /etc/dnsmasq.conf file on the dnsmasq server / squid server and tested it with a web page on my desktop. It successfully blocked the China based page and put up “Hi!” instead:

    # Experimental block of all things China .cn domain                                                                           

    Where would be the IP address of your web server (like or whatever).

    Note that what is between the / / marks is the ‘wild card’. This says “Anything ending in .cn’

    Nice. I can now wildcard block whole swathes of the world where I have zero interest in connecting. Like, oh, Nigeria ;-) (Doesn’t stop mail from them, but does stop any dns resolves from going to that domain name… does still leave .com and .info and others open to abuse, though, even if the user is located in Nigeria or China… For that, I need to do wildcard IP number range blocks. I’ve usually done them with explicit hard routes, so for example route all traffic to an internal dead end like as a port on an interior firewall.

    root@R_Pi_DebJ_DD:/var/www/html# nslookup
    Non-authoritative answer:

    so one could route 202.x.x.x to nowhere and block all things in that chunk of the internet. (It would be a good idea to find out what all is in there first… This China block might just be instead of the whole thing…)

    So for now I’ll just be picking off some countries I never want to talk to… ( I can always bypass the block, on my workstation, by pointing my browser outside the proxy and my DNS at an outside server) and adding their top level domains to the block list. Eventually I’ll also pick some IP blocks assigned to those countries and route them down a rat-hole too. Yeah, overkill… but I like overkill ;-0

    A couple of example ‘route’ commands from the route man page to show how to route things:

           route add -net netmask dev lo
                  adds the normal loopback  entry,  using  netmask  and
                  associated with the "lo" device (assuming this device was previā€
                  ously set up correctly with ifconfig(8)).
           route add -net netmask dev eth0
                  adds a route to the local network 192.56.76.x via  "eth0".   The
                  word "dev" can be omitted here.
           route add -net netmask reject
                  This   installs  a  rejecting  route  for  the  private  network

    Note that you could:
    route add -net netmask dev lo
    to route that network to the loopback interface or you could
    route add -net netmask dev eth1
    and send it to a non-existing or down ethernet interface or you could
    route add -net netmask reject
    to just flat out reject it. Or you could
    route add -net gw
    to send such ip traffic to and let him handle it as a gateway.

    There’s lots of ways to send the traffic somewhere that’s a dead end. (Typically you would use the ‘reject’ line in a router, but sometimes you want to send it to a place for inspection, like you could route it to your Intrusion Detection System IP address so then it gets looked at…)

    These folks list all the IP ranges for China:

    Well, that’s for another day. Now I think it time to ‘quit while I’m ahead’ and spend a bit of time enjoying that things are working now…

  3. E.M.Smith says:

    Got tired of the (android) Tablet and various browser defaults tossing me into when I use duckduckgo by preference… so I added an entry to my DNS server /etc/hosts file:

    Now when something tries yet again to toss me to google, a duckduckgo search panel pops up instead. 8-)

    AT&T kept trying to “help” (as many ISPs do) by tossing ‘failed URLs in a browser’ to their custom search engine. So I’ve also got a new resolution for ‘’ too… they don’t need to be sniffing my shorts either…

    Oh, and a nice feature of the DVD sized Knoppix, it has a port scanner built in… Looks like my boundary router only has www and dns ports open by default. Nice.

    Doing a DNS lookup for an inside machine (dmz box) against the telco router dns service had it report name and IP of my DMZ box (really a Pi card)… so for real prevention of info leakage, I’d need an isolation router between the Telco router and everything else.

    It is nice to be scratching some of these itches at last. Chopping off the tentacles of data suckage one at a time…

    DNS of your own is a very effective way to control what machines can talk to which destinations based on names. Routing directives let you do the same based on IP numbers. Firewalls via policies and traffic type. All 3 together makes a formidable barrier… though a lot of work to set up.

    FWIW, the musl libraries don’t support the dns “search” function, so Alpine musl would be a bad choice for industrial DNS at this time (they are fixing it), but good enough for home use (I’m not doing 100 virtual machines and containers with resolv.conf rewrite rules and more… and don’t need ‘search’. This came up when nslookup didn’t know the name of its own server…) Folks needing those features need to use a clib based system.

  4. beng135 says:

    Thanks EM — pretty good tutorial on dnsmasq settings & some new things I might tinker with.

    Couple questions. Mine is the simplest of dnsmasq setups — stand-alone machine, no networking, just an internet connection w/the winhost2002 host file (14000 entries!). In /etc/dnsmasq.conf I have uncommented:

    # Or you can specify which interface _not_ to listen on

    Read somewhere it was a good thing for my situation & not sure why dnsmasq would need to listen to eth0. Everything works — typing:


    returns for its IP number as expected & typing in a browser address bar says “unable to connect”.

    Puppy Linux always rewrites the /etc/resolv.conf at startup, so adding custom dns nameservers was a puzzle, but tinkering found that adding nameservers to /etc/resolv.conf.head got them added at the top of the nameserver lookup list.

  5. Pingback: Alpine Lunix IDS – Snort no Suricata | Musings from the Chiefio

  6. E.M.Smith says:


    You are most welcome!

    In your example, I think the “except” line is to prevent DNS serving out that interface, not listening, and this prevents your box arguing with the Telco router to the internet about what IP maps to which name… (which likely doesn’t matter as you don’t have some other box on that segment of network, but, say, you booted a laptop and it was in that network, now it has TWO servers for DNS. It would likely just go with the DHCP one from the Telco, but it is configurable. IFF you put both servers in it as DNS providers, it would be getting different values from the two servers…)

    In any case, by turning off service on eth0 you are effectively saying “Serve DNS to yourself from your hosts table and then your upstream DNS sources, but don’t serve DNS to anyone else on the ethernet wire, just use it to get to your upstream.”

    Why block an interface? Well, for example, I have my “DMZ Services Pi Board” in my “DMZ” providing DNS services. But my Telco router does that too. Lets say I added a WiFi dongle to the Pi so it had eth0 and wlan0, but I only wanted to use eth0 for “internet things” and wanted to use the Pi ONLY to serve internal dns to my internal boxes (via the wifi link) and have the Telco box serve addresses into the DMZ. That is, have the DMZ hosts be blissfully unaware of the ‘inside’ IP world (so if hacked could divulge nothing, or if asked for addresses the DNS on the Pi would be mum about it). For that, I’d specify that the Pi was to serve DNS requests (internal hosts only being served with internal IPs only) via wlan0, but would NOT pay any attention to the eth0 port for DNS queries so info could not leak AND so that the Telco router was the DNS provider in that zone.

    Then folks connecting to the Telco router could get to, say, questionable sites, but folks on the ‘inside’ would be getting the ‘grounded’ IP service. Now I can use a DMZ host to visit a site (using a sanitized Live Boot system) and see what it does and is it really evil? While still protecting inside hosts via DNS blocks.

    My internal network can get both the private network hosts AND external host lookup from the Pi, but machines in the DMZ can only see what the Telco knows in terms of DNS lookup.

    Another example? PXE boot. I have PXE running on an isolated subnet with its own numbering plan behind yet another WiFi router (yes, that makes it ‘3 hops’ to get out to the internet…) PXE expects to be the DNS provider to the PXE booted systems. I’ve set my PXE boot server up to NOT provide DNS (and let it come from the WiFi / PXE router), but an equally valid approach would be to have PXE serve DNS to the subnet it runs, and (for some unknown reason…) have my inside services Pi board with an interface into the PXE net and a separate interface into the ‘rest of my inside network’ outside the PXE WiFi router. Perhaps to provide print spooling or a inside web server or squid service to the PXE boot hosts (all without needing to go to the DMZ and be exposed to the DMZ level of security). Yet while I want my inside Pi services card to provide DNS inside to my private side world, I don’t want it to provide DNS into the PXE boot subnet. In that case, I shut off DNS service on whatever interface connects to the PXE boot subnet.

    internet Telco router->DMZ w/Pi services and DNS cache server->private WiFi & router->inside Pi services->PXE WiFi & router-> PXE server-> PXE boot clients

    Now add a link from that ‘inside Pi services’ card into the PXE subnet, perhaps via a USB ethernet dongle or perhaps just via a WiFi dongle to the PXE WiFi router. I want all the services that inside Pi server provides except DNS as that’s gotten from the PXE server…

    Essentially, it lets you control which bits of your network subnets get DNS from which sources and that lets you control what information is seen in outer layers (security / privacy) and it lets you isolate service loads to different sources (so PXE can be configured stand alone and not dependent on that outside service, for example).

  7. beng135 says:

    Thanks, tho much of what you explain is beyond my understanding. :)

Comments are closed.