Lighttpd & certbot certificates for mutiple vhosts

Hi All,

I have lightty serving a vhost with letsencrypt / certbot on DietPi for a long time working great.

I now want to add another vhost and cert.

I have created another directory and matching vhost in /etc/lighttpd/lighttpd.conf which is working fine on plain http

I have generated new cert with ;

# certbot certonly --webroot -w /mnt/webdir/http2 -d www.newdomain.com

I have checked /etc/letsencrypt/live/ which now has a directory www.newdomain.com
In that directory are symlinks to /etc/letsencrypt/archive/www.newdomain.com
with files cert1.pem, chain1.pem, fullchain1.pem and privkety1.pem

I have restarted lighty and checked with https://www.sslshopper.com/ssl-checker.html

however the test reports that only the certificate for the existing vhost is being served and not the new one?

Are there any steps I have missed or can you give me any pointers of things to check?

Thanks!

Would I have to combine the certs for all the vhosts into one certbot command?

can you share the lightty configuration your have created.

I’ll post my lighty config shortly but just wanted to report the results from this morning;

I was reading through the docs here https://eff-certbot.readthedocs.io/en/stable/using.html#getting-certificates-and-choosing-plugins
and especially the section ‘Webroot’ which seems relevant in my case and gives this example;

certbot certonly --webroot -w /var/www/example -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net

I used the above example to ‘expand’ the existing certificate and it all looked successful. I restarted lighty then decided to reboot the pi.

Anyway, on doing an external SSL test I still get reports that whilst the original domain name is serving SSL the added one isn’t

My anonymized lightpd.conf

### domain1 ############################################################
$HTTP["host"] =~ "(^|\.)domain1\.co.uk$" {
    server.document-root        = "/mnt/webdir/http"
    server.error-handler-404    = "/404.htm"
}
$HTTP["scheme"] == "http" {
    $HTTP["host"] =~ "(^|\.)domain1\.co.uk$" {
        url.redirect = ( "^/(.*)" => "https://www.domain1.co.uk/$1" )
    }
}
### newdomain ###########################################################
$HTTP["host"] =~ "(^|\.)newdomain\.co.uk$" {
server.document-root        = "/mnt/webdir/http2"
server.error-handler-404    = "/error404.php"
}

$HTTP["scheme"] == "http" {
    $HTTP["host"] =~ "(^|\.)newdomain\.co.uk$" {
        url.redirect = ( "^/(.*)" => "https://www.newdomain.co.uk/$1" )
    }
}
##########################################################################

server.modules              = ( "mod_expire", "mod_access",	"mod_alias", 	"mod_rewrite", "mod_redirect", "mod_setenv", "mod_cgi", "mod_openssl", "mod_deflate" )
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.document-root        = "/mnt/webdir/http"
server.port                 = 80
server.max-worker	          = 4
index-file.names            = ( "index.html", "index.htm", "index.php" )
fastcgi.map-extensions      = ( ".htm" => ".php", ".html" => ".php")
dir-listing.activate        = "enable"
server.tag                  = ""

deflate.mimetypes = ("text/html", "text/plain", "text/css", "text/javascript", "text/xml")
deflate.allowed-encodings = ( "br", "gzip", "deflate" ) # "bzip2" and "zstd" also supported
expire.url = ("https://content-security-policy.com/font-src//" => "access plus 7 days")

cgi.assign = ( ".pl"  => "/usr/bin/perl", ".cgi" => "/usr/bin/perl" )
alias.url += ( "/cgi-bin/" => "/mnt/webdir/cgi-bin/" )

include_shell "/usr/share/lighttpd/create-mime.conf.pl"
include "/etc/lighttpd/conf-enabled/*.conf"

setenv.add-response-header  = (
  "Content-Security-Policy" => "default-src 'self'; style-src 'self' 'unsafe-inline'",
	"Strict-Transport-Security" => "max-age=15768000",
	"X-Content-Type-Options" => "nosniff",
	"X-Frame-Options" => "DENY",
	"X-XSS-Protection" => "1; mode=block",
	"Referrer-Policy" => "no-referrer-when-downgrade",
	"Permissions-Policy" => "geolocation=(self 'https://www.domain1.co.uk')"
)

I am starting to wonder if this is to do with document root location?

not sure if I understood your setup fully.

You have a domain 1 that is working well with the Lighttpd + SSL setup
Now you have a domain 2 that should be served by Lighttpd as well. However SSL for this new domain is not working?

Looking to your config, did you specified your new SSL certificates somewhere?

You have a domain 1 that is working well with the Lighttpd + SSL setup [Correct]
Now you have a domain 2 that should be served by Lighttpd as well. However SSL for this new domain is not working? [Correct]

So far I have added another vhost and created another directory to store newdomain
The I followed the docs and ran similar to;

certbot certonly --webroot -w /mnt/webdir/http -d www.domain1.co.uk -d domain1.co.uk -w /mnt/webdir/http2 -d www.newdomain.co.uk -d newdomain.co.uk

I chose the expand option and all looked good / successful from the certbot output [sorry didint save it as I rebooted]

As above I am wondering if a combined cert can only be served from the doc root specified in the main section of lighttpd.conf and has no relevance to the -w /path options passed to certbot ie even multiple certs are served only on server.document-root= and not the individual locations of the vhosts

Ed: Just tested the theory in the above paragraph by amending the server.document-root= directive in the main part of lighttpd.conf and it hasn’t made any difference, it’s still only domain1 cert that is being served.

Out of interest this is the output from https://www.ssllabs.com/ssltest/analyze.html?d=www.newdomain.co.uk made relevant to this post when testing newdomain

Certificate name mismatch
Try these other domain names (extracted from the certificates): www.domain1.co.uk

What does this mean?

We were able to retrieve a certificate for this site, but the domain names listed in it do not match the domain name you requested us to inspect. It’s possible that:

The web site does not use SSL, but shares an IP address with some other site that does. [Ed: Yes!]
The web site no longer exists, yet the domain name still points to the old IP address, where some other site is now hosted.
The web site uses a content delivery network (CDN) that does not support SSL.
The domain name is an alias for a web site whose main name is different, but the alias was not included in the certificate by mistake.

ok I got it working on my test system. Some configuration is needed to have 2 domains with 2 different SSL certs.

Basically the first domain I created using plain installation of Lighttpd & certbot. Certificate was created using dietpi-letsencrypt. That’s our the default setup.

Now we need to create the 2nd set of certificated for the 2nd domain

certbot certonly --webroot -w /var/www2 -d my2.domain.com

This will create a new directory inside /etc/letsencrypt/live/

ok let’s setup the 2nd domain

mkdir /var/www2		# or whatever web root you like
nano /etc/lighttpd/conf-enabled/20-2nd-domain.conf

I added following to specify web root and the certificates for the 2nd domain. Remove the IPv6 stuff if not needed.

$HTTP["host"] =~ "(^|\.)my2.domain.com$" {
server.document-root = "/var/www2"
server.errorlog = "/var/log/lighttpd/error2.log"
$SERVER["socket"] == ":443" {
	protocol = "https://"
	ssl.engine = "enable"
	ssl.pemfile = "/etc/letsencrypt/live/my2.domain.com/fullchain.pem"
	ssl.privkey = "/etc/letsencrypt/live/my2.domain.com/privkey.pem"
	ssl.ca-file = "/etc/letsencrypt/live/my2.domain.com/fullchain.pem"

	# For DH/DHE ciphers, dhparam should be >= 2048-bit
	#ssl.dh-file = "/path/to/dhparam.pem"
	# ECDH/ECDHE ciphers curve strength, see "openssl ecparam -list_curves"
	ssl.ec-curve = "secp384r1"

	# Environment flag for HTTPS enabled
	setenv.add-environment = ( "HTTPS" => "on" )

	# Intermediate configuration, tweak to your needs
	ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2")
	ssl.cipher-list = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
	ssl.honor-cipher-order = "disable"
	ssl.disable-client-renegotiation = "enable"
	}
# IPv6
$SERVER["socket"] == "[::]:443" {
	protocol = "https://"
	ssl.engine = "enable"
	ssl.pemfile = "/etc/letsencrypt/live/my2.domain.com/fullchain.pem"
	ssl.privkey = "/etc/letsencrypt/live/my2.domain.com/privkey.pem"
	ssl.ca-file = "/etc/letsencrypt/live/my2.domain.com/fullchain.pem"

	# For DH/DHE ciphers, dhparam should be >= 2048-bit
	#ssl.dh-file = "/path/to/dhparam.pem"
	# ECDH/ECDHE ciphers curve strength, see "openssl ecparam -list_curves"
	ssl.ec-curve = "secp384r1"

	# Environment flag for HTTPS enabled
	setenv.add-environment = ( "HTTPS" => "on" )

	# Intermediate configuration, tweak to your needs
	ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2")
	ssl.cipher-list = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
	ssl.honor-cipher-order = "disable"
	ssl.disable-client-renegotiation = "enable"
	}
}

ok last step is to ensure certificates for domain 1 are used in case this one is accessed only.

nano /etc/lighttpd/conf-enabled/50-dietpi-https.conf

Basically I added 2 lines. First one right below server.modules += ( “mod_openssl” ). Like this

server.modules += ( "mod_openssl" )
$HTTP["host"] =~ "(^|\.)my1.domain.com$" { 		# new line

and to close the $HTTP[“host”] block a } at the end/last line

finally restart Lighttpd

systemctl restart lighttpd.service

Close the browser and try again.

Whole stuff is based on following 2 Lighttpd forum posts.

https://redmine.lighttpd.net/boards/2/topics/9612?r=9615#message-9615
https://redmine.lighttpd.net/boards/2/topics/546

Wow - that’s… blinks… A-m-a-z-i-n-g-!!!

I had got the first part done but couldn’t get it working and assumed the only way was a combined certificate. I have asked similar on the LE forum and will let you know if there is anything more ‘elegant’

But this is great thanks will give it a try next :slight_smile:

Thanks for all your input :slight_smile:

As it’s so long ago I can’t remember what info the dietpi-letsencrypt dialogue asks you but just in case anyone else is doing this please bear in mind that as far as certbot is concerned domain.com and www.domain.com are two different domains and you would need two certificates or a combined certificate and as yet I have not figured out how to get a combined certificate to work. If anyone else has please let us kno!

nano /etc/lighttpd/conf-enabled/50-dietpi-https.conf
Basically I added 2 lines. First one right below server.modules += ( "mod_openssl" ). Like this
server.modules += ( "mod_openssl" )
$HTTP["host"] =~ "(^|\.)my1.domain.com$" { 		# new line
and to close the $HTTP["host"] block a } at the end/last line

The line server.modules += ( “mod_openssl” ) does not exist in my /etc/lighttpd/conf-enabled/50-dietpi-https.conf ?

I’ll post it here;

# Based on: https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=lighttpd 1.4.35&openssl=1.0.1t&hsts=yes&profile=intermediate
$SERVER["socket"] == ":443" {
	protocol     = "https://"
	ssl.engine   = "enable"
	ssl.disable-client-renegotiation = "enable"

	# pemfile is cert+privkey, ca-file is the intermediate chain in one file
	ssl.pemfile               = "/etc/letsencrypt/live/www.cpcnw.co.uk/combined.pem"
	ssl.ca-file               = "/etc/letsencrypt/live/www.cpcnw.co.uk/fullchain.pem"

	# for DH/DHE ciphers, dhparam should be >= 2048-bit
	#ssl.dh-file               = "/path/to/dhparam.pem"
	# ECDH/ECDHE ciphers curve strength (see 'openssl ecparam -list_curves')
	ssl.ec-curve              = "secp384r1"
	# Compression is by default off at compile-time, but use if needed
	# ssl.use-compression     = "disable"

	# Environment flag for HTTPS enabled
	setenv.add-environment = (
		"HTTPS" => "on"
	)

	# intermediate configuration, tweak to your needs
	ssl.use-sslv2 = "disable"
	ssl.use-sslv3 = "disable"
	ssl.honor-cipher-order    = "enable"
	ssl.cipher-list           = "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"
}

It looks to me that this could be included in lighttpd.conf under the individual vhosts?

In any caseI have added the the following in my vhost section for the second domain;

$HTTP["host"] =~ "(^|\.)my2.domain.com$" {
server.document-root = "/var/www2"
server.errorlog = "/var/log/lighttpd/error2.log"
$SERVER["socket"] == ":443" {
	protocol = "https://"
	ssl.engine = "enable"
	ssl.pemfile = "/etc/letsencrypt/live/my2.domain.com/fullchain.pem"
	ssl.privkey = "/etc/letsencrypt/live/my2.domain.com/privkey.pem"
	ssl.ca-file = "/etc/letsencrypt/live/my2.domain.com/fullchain.pem"

	ssl.ec-curve = "secp384r1"
	setenv.add-environment = ( "HTTPS" => "on" )
	ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2")
	ssl.cipher-list = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128.....
	ssl.honor-cipher-order = "disable"
	ssl.disable-client-renegotiation = "enable"
	}

Restarted and tested! It mostly passes the https://www.ssllabs.com/ssltest/analyze.html test apart from one section which complains about a mismatch and references domain1

Also, a web browser test shows as not secure and a red line through https

Bit closer though…

OK as that didn’t quite work I tried your suggestion of adding a file /etc/lighttpd/conf-available/20-2nd-domain.conf and symlink /etc/lighttpd/conf-enabled/20-2nd-domain.conf as described in the README in that directory.

That halts lighty completely :frowning:

probably you played to much on configuration files. Usually this should not be needed as majority of thinks could be done via drop-in config files. Before creating new files, you should revert the changes you have done before to avoid conflicts. Basically you could test the setup as follow

/usr/sbin/lighttpd -tt -f /etc/lighttpd/lighttpd.conf

This should let you know what the issue is.

I use 2 domains (but on the same webpath) within the same certificate and all I had to do is to comma-seperate my domains in the dialogue. As far as I can remember there was also a hint about this in the dialogue.
The same goes for -d flag when you use the certbot command on CLI. After the flag you can add domains by comma-seperate them:
certbot certonly --webroot -w /mnt/webdir/ExampleHTTP -d domain1.org,www.domain1.org,anotherdomain.net

Yep, you are right I have gone back to my original config and it works fine - only with one domain [the first one with dietpi-letsencrypt] and no other vhosts with SSL which is a shame.

I will come back to this later. Spent most of my Saturday off and no results :frowning:

for me it was working nearly ootb with the setup I have describe above. The import part was to modify 50-dietpi-https.conf and to add the $HTTP[“host”] definition to specify for which domain the certificate is valid.

Can you post your 50-dietpi-https.conf please?

Its OK I have it sorted now 9am-6pm - nice weekend off work lol! I have gone for the totally manual route…

I commented everything out of 50-dietpi-https.conf I then put the whole $SERVER[“socket”] == “:443” section right at the top of my lighttpd.conf with the deafult domain pem file reference
I then put the new / additional domain pem file references under their relevant vhost sections

Restarted everything and it now works fine so happy again! If you are interested this is the top section;

$SERVER["socket"] == ":443" {
protocol = "https://"
ssl.engine = "enable"
ssl.pemfile  = "/etc/letsencrypt/live/www.default.co.uk/combined.pem"
ssl.ca-file  = "/etc/letsencrypt/live/www.default.co.uk/fullchain.pem"
ssl.ec-curve  = "secp384r1"
setenv.add-environment = ( "HTTPS" => "on" )
ssl.use-sslv2 = "disable"
ssl.use-sslv3 = "disable"
ssl.honor-cipher-order  = "enable"
ssl.cipher-list  = "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"
}

$HTTP["host"] =~ "(^|\.)default\.co.uk$" {
    server.document-root        = "/var/www/http"
    server.error-handler-404    = "/404.htm"
}
$HTTP["scheme"] == "http" {
    $HTTP["host"] =~ "(^|\.)default\.co.uk$" {
        url.redirect = ( "^/(.*)" => "https://www.default.co.uk/$1" )
    }
}

$HTTP["host"] =~ "(^|\.)additional\.co.uk$" {
server.document-root        = "/var/www/http2"
server.error-handler-404    = "/error404.php"
ssl.pemfile = "/etc/letsencrypt/live/www.additional.co.uk/combined.pem"
}
$HTTP["scheme"] == "http" {
    $HTTP["host"] =~ "(^|\.)additional\.co.uk$" {
        url.redirect = ( "^/(.*)" => "https://www.additional.co.uk/$1" )
    }
}

Note: I had to cat / combine the privkey.pem and cert.pem into a single pem file [combined.pem or what you want to call it]

This is the important part of the Lighty docs https://redmine.lighttpd.net/projects/1/wiki/docs_ssl#Server-Name-Indication-SNI