changeset 2093:c0847b30833f ssltesting

mv https.luan Https.luan
author Franklin Schmidt <fschmidt@gmail.com>
date Sat, 13 Dec 2025 20:25:56 -0700
parents 429827024f4e
children 3c40cb15d468
files host/test/test_https.luan src/luan/host/Https.luan src/luan/host/https.luan src/luan/host/init.luan
diffstat 4 files changed, 193 insertions(+), 192 deletions(-) [+]
line wrap: on
line diff
--- a/host/test/test_https.luan	Fri Dec 12 18:53:15 2025 -0800
+++ b/host/test/test_https.luan	Sat Dec 13 20:25:56 2025 -0700
@@ -3,19 +3,18 @@
 local do_file = Luan.do_file or error()
 local Io = require "luan:Io.luan"
 local uri = Io.uri or error()
-local Hosted = require "luan:host/Hosted.luan"
 local Logging = require "luan:logging/Logging.luan"
 local logger = Logging.logger "test_https"
 
 
-do_file "classpath:luan/host/https.luan"
+local Https = require "classpath:luan/host/Https.luan"
 
 local is_https = true
-local domain = "https.s3.luan.software"
+local domain = "https.me.luan.software"
 local site_dir = uri("file:local")
 local luanhost_dir = uri("file:..")
 local dry_run = true
 
 site_dir.mkdir()
 
-Hosted.do_set_https(is_https,domain,site_dir,luanhost_dir,dry_run)
+Https.do_set_https(is_https,domain,site_dir,luanhost_dir,dry_run)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/host/Https.luan	Sat Dec 13 20:25:56 2025 -0700
@@ -0,0 +1,187 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local new_error = Luan.new_error or error()
+local load_file = Luan.load_file or error()
+local ipairs = Luan.ipairs or error()
+local Io = require "luan:Io.luan"
+local ip = Io.ip or error()
+local uri = Io.uri or error()
+local String = require "luan:String.luan"
+local starts_with = String.starts_with or error()
+local Thread = require "luan:Thread.luan"
+local try_synchronized = Thread.try_synchronized or error()
+local Http = require "luan:http/Http.luan"
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "https"
+
+
+local Https = {}
+
+local my_ips = Io.my_ips()
+
+local function do_set_https(is_https,domain,site_dir,luanhost_dir,dry_run)
+	local nginx_file = site_dir.child("nginx.ssl.conf")
+
+	-- TODO: implement this later
+	local ssl_files_dir = site_dir--.child("ssl/")
+	-- ssl_files_dir.mkdir()
+
+	local key_file = ssl_files_dir.child(domain..".key")
+	local csr_file = ssl_files_dir.child(domain..".csr")
+	local tmp_cert_out = ssl_files_dir.child(domain..".crt.tmp")
+	local local_cer_file = ssl_files_dir.child("fullchain.cer")
+	-- luan/host
+	local luanhost_file = "file:"..luanhost_dir.to_string().."/"
+	local luanhost_dir_str = luanhost_dir.canonical().to_string()
+	local changed = false
+
+	if is_https then -- https
+		if not key_file.exists() \
+			or not local_cer_file.exists() or local_cer_file.length()==0 \
+			or not nginx_file.exists() \
+		then
+			local domain_ip = ip(domain)
+			local is_local = domain_ip == "127.0.0.1"
+			logger.info("is_local "..is_local)
+
+			-- Use openssl directly to make a self-signed cert,
+			-- no external cert authority involved
+			if is_local then
+				local ssl_files_dir_str = ssl_files_dir.canonical().to_string().."/";
+				local cmd = [[
+					openssl req -x509 -newkey rsa:2048 -nodes \
+						-keyout ]]..ssl_files_dir_str..domain..[[.key \
+						-out ]]..ssl_files_dir_str..[[fullchain.cer -days 365 \
+						-subj "/CN=]]..domain..[[" \
+						-addext "subjectAltName=DNS:]]..domain..[[,IP:127.0.0.1"
+				]]
+				logger.info("local ssl commandline:\n"..cmd)
+				local s = uri("bash:"..cmd).read_text()
+				logger.info("issue local certificate")
+			else
+				if my_ips[domain_ip] ~= true then
+					logger.error("the domain "..domain.." doesn't map to this machine")
+					return
+				end
+				try
+					-- CHANGEME
+					dry_run = true
+
+					-- make the challenge dir. note that this is
+					-- directly under sites/DOMAIN, and *not* under
+					-- sites/DOMAIN/site.
+					local acme_challenges = site_dir.child("acme-challenge/")
+					acme_challenges.mkdir()
+
+					-- Create a domain key to sign the certificate signing request (csr).
+					local key_file_str = key_file.canonical().to_string()
+					local cmd = "openssl genrsa 4096 > "..key_file_str
+					local s = uri("bash:"..cmd).read_text()
+					logger.info("create domain key\n"..s)
+
+					-- Create the csr.
+					local csr_file_str = csr_file.canonical().to_string()
+					local cmd = 'openssl req -new -sha256 -key '..key_file_str..' -subj "/CN='..domain..'" > '..csr_file_str
+					local s = uri("bash:"..cmd).read_text()
+					logger.info("create csr\n"..s)
+
+					-- Finally, get our cert from letsencrypt.
+					local cmd = luanhost_dir_str..[[/acme_tiny --account-key ]]..luanhost_dir_str..[[/local/tiny_account.key \
+						--csr ]]..csr_file_str..[[ \
+						--acme-dir ]]..acme_challenges.canonical().to_string()..[[ \
+					]]
+
+					-- TODO: this often doesn't work and I don't know if it's
+					-- because of this code or because of letsencrypt.
+					-- fix if broken.
+					if dry_run then
+						local dry_run_dir_url = "https://acme-staging-v02.api.letsencrypt.org/directory"
+						cmd = cmd.." --directory-url "..dry_run_dir_url
+					end
+					cmd = cmd.." > "..tmp_cert_out.canonical().to_string()
+					logger.info("acme-tiny commandline:\n"..cmd)
+
+					local s = uri("bash:"..cmd).read_text()
+					logger.info("get cert signed by letsencrypt\n"..s)
+
+					-- Empty stdout from acme-tiny is a failure.
+					if tmp_cert_out.length() == 0 then
+						-- TODO: this should fail non-gracefully,
+						-- all failures here are almost certainly bugs.
+						logger.error("FAILED getting cert from letsencrypt.\nSee previous output.\nNot writing to fullchain.cer")
+					else
+						-- Success! Move the temp output to the real fullchain.
+						local tmp_out_str = tmp_cert_out.canonical().to_string()
+						local local_cer_file_str = local_cer_file.canonical().to_string()
+
+						local cmd = "mv "..tmp_out_str.." "..local_cer_file_str
+						local s = uri("bash:"..cmd).read_text()
+						logger.info("move temp output to fullchain.cer\n"..s)
+					end
+
+				catch e
+					logger.error("Error setting up ACME: "..e)
+				end_try
+
+			end
+				-- We now have our certificate!
+				-- Now we just need to generate the nginx config
+				-- that uses it, place it in luan/host/sites/*/nginx.ssl.conf
+				-- and tell luan-host to reload nginx.
+
+			if key_file.exists() and local_cer_file.exists() and local_cer_file.length() > 0 then
+				changed = true
+				-- the nginx config only requires 2 files:
+				-- fullchain.cer and DOMAIN.key
+				logger.info("writing nginx conf to "..nginx_file.canonical().to_string())
+				local conf = load_file(luanhost_file.."startup/nginx/nginx.ssl.conf.luan")
+				local nginx = ` conf(luanhost_dir_str,domain) `
+				nginx_file.write(nginx)
+			end
+		end
+	else -- http
+		if key_file.exists() or nginx_file.exists() then
+			changed = true
+			nginx_file.delete()
+			local_cer_file.delete()
+			local ptn = domain.."."
+			for _, file in ipairs(site_dir.children()) do
+				if starts_with(file.name(),ptn) then
+					file.delete()
+				end
+			end
+		end
+	end
+	if changed then
+		local cmd = [[
+sudo $(which nginx) -t -c "]]..luanhost_dir_str..[[/local/nginx.conf" && \
+sudo $(which nginx) -s reload -c "]]..luanhost_dir_str..[[/local/nginx.conf";
+]]
+		local s = uri("bash:"..cmd).read_text()
+		logger.info("reload_nginx "..s)
+	end
+	--logger.info "done"
+end
+Https.do_set_https = do_set_https	-- for testing
+
+function Https.set_https(is_https)
+	if Http.did_init() then
+		logger.error(new_error("set_https called outside of init.luan"))
+		return
+	end
+	local domain = Http.domain
+	local site_dir = uri("site:").parent()
+	local luanhost_dir = uri("file:.")
+
+	-- use for testing, so as to not hit rate limits
+	-- on the real letsencrypt servers
+	local dry_run = false
+
+	if not try_synchronized( function()
+		do_set_https(is_https,domain,site_dir,luanhost_dir,dry_run)
+	end, domain..".lock", 0 )() then
+		logger.info("set_https already running for "..domain..", skipping")
+	end
+end
+
+return Https
--- a/src/luan/host/https.luan	Fri Dec 12 18:53:15 2025 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local new_error = Luan.new_error or error()
-local load_file = Luan.load_file or error()
-local ipairs = Luan.ipairs or error()
-local Boot = require "luan:Boot.luan"
-local Io = require "luan:Io.luan"
-local ip = Io.ip or error()
-local uri = Io.uri or error()
-local String = require "luan:String.luan"
-local starts_with = String.starts_with or error()
-local Thread = require "luan:Thread.luan"
-local try_synchronized = Thread.try_synchronized or error()
-local Http = require "luan:http/Http.luan"
-local Hosted = require "luan:host/Hosted.luan"
-local Logging = require "luan:logging/Logging.luan"
-local logger = Logging.logger "https"
-
-
-local my_ips = Io.my_ips()
-
-local function do_set_https(is_https,domain,site_dir,luanhost_dir,dry_run)
-	local nginx_file = site_dir.child("nginx.ssl.conf")
-
-	-- TODO: implement this later
-	local ssl_files_dir = site_dir--.child("ssl/")
-	-- ssl_files_dir.mkdir()
-
-	local key_file = ssl_files_dir.child(domain..".key")
-	local csr_file = ssl_files_dir.child(domain..".csr")
-	local tmp_cert_out = ssl_files_dir.child(domain..".crt.tmp")
-	local local_cer_file = ssl_files_dir.child("fullchain.cer")
-	-- luan/host
-	local luanhost_file = "file:"..luanhost_dir.to_string().."/"
-	local luanhost_dir_str = luanhost_dir.canonical().to_string()
-	local changed = false
-
-	if is_https then -- https
-		if not key_file.exists() \
-			or not local_cer_file.exists() or local_cer_file.length()==0 \
-			or not nginx_file.exists() \
-		then
-			local domain_ip = ip(domain)
-			local is_local = domain_ip == "127.0.0.1"
-			logger.info("is_local "..is_local)
-
-			-- Use openssl directly to make a self-signed cert,
-			-- no external cert authority involved
-			if is_local then
-				local ssl_files_dir_str = ssl_files_dir.canonical().to_string().."/";
-				local cmd = [[
-					openssl req -x509 -newkey rsa:2048 -nodes \
-						-keyout ]]..ssl_files_dir_str..domain..[[.key \
-						-out ]]..ssl_files_dir_str..[[fullchain.cer -days 365 \
-						-subj "/CN=]]..domain..[[" \
-						-addext "subjectAltName=DNS:]]..domain..[[,IP:127.0.0.1"
-				]]
-				logger.info("local ssl commandline:\n"..cmd)
-				local s = uri("bash:"..cmd).read_text()
-				logger.info("issue local certificate")
-			else
-				if my_ips[domain_ip] ~= true then
-					logger.error("the domain "..domain.." doesn't map to this machine")
-					return
-				end
-				try
-					-- CHANGEME
-					dry_run = true
-
-					-- make the challenge dir. note that this is
-					-- directly under sites/DOMAIN, and *not* under
-					-- sites/DOMAIN/site.
-					local acme_challenges = site_dir.child("acme-challenge/")
-					acme_challenges.mkdir()
-
-					-- Create a domain key to sign the certificate signing request (csr).
-					local key_file_str = key_file.canonical().to_string()
-					local cmd = "openssl genrsa 4096 > "..key_file_str
-					local s = uri("bash:"..cmd).read_text()
-					logger.info("create domain key\n"..s)
-
-					-- Create the csr.
-					local csr_file_str = csr_file.canonical().to_string()
-					local cmd = 'openssl req -new -sha256 -key '..key_file_str..' -subj "/CN='..domain..'" > '..csr_file_str
-					local s = uri("bash:"..cmd).read_text()
-					logger.info("create csr\n"..s)
-
-					-- Finally, get our cert from letsencrypt.
-					local cmd = luanhost_dir_str..[[/acme_tiny --account-key ]]..luanhost_dir_str..[[/local/tiny_account.key \
-						--csr ]]..csr_file_str..[[ \
-						--acme-dir ]]..acme_challenges.canonical().to_string()..[[ \
-					]]
-
-					-- TODO: this often doesn't work and I don't know if it's
-					-- because of this code or because of letsencrypt.
-					-- fix if broken.
-					if dry_run then
-						local dry_run_dir_url = "https://acme-staging-v02.api.letsencrypt.org/directory"
-						cmd = cmd.." --directory-url "..dry_run_dir_url
-					end
-					cmd = cmd.." > "..tmp_cert_out.canonical().to_string()
-					logger.info("acme-tiny commandline:\n"..cmd)
-
-					local s = uri("bash:"..cmd).read_text()
-					logger.info("get cert signed by letsencrypt\n"..s)
-
-					-- Empty stdout from acme-tiny is a failure.
-					if tmp_cert_out.length() == 0 then
-						-- TODO: this should fail non-gracefully,
-						-- all failures here are almost certainly bugs.
-						logger.error("FAILED getting cert from letsencrypt.\nSee previous output.\nNot writing to fullchain.cer")
-					else
-						-- Success! Move the temp output to the real fullchain.
-						local tmp_out_str = tmp_cert_out.canonical().to_string()
-						local local_cer_file_str = local_cer_file.canonical().to_string()
-
-						local cmd = "mv "..tmp_out_str.." "..local_cer_file_str
-						local s = uri("bash:"..cmd).read_text()
-						logger.info("move temp output to fullchain.cer\n"..s)
-					end
-
-				catch e
-					logger.error("Error setting up ACME: "..e)
-				end_try
-
-			end
-				-- We now have our certificate!
-				-- Now we just need to generate the nginx config
-				-- that uses it, place it in luan/host/sites/*/nginx.ssl.conf
-				-- and tell luan-host to reload nginx.
-
-			if key_file.exists() and local_cer_file.exists() and local_cer_file.length() > 0 then
-				changed = true
-				-- the nginx config only requires 2 files:
-				-- fullchain.cer and DOMAIN.key
-				logger.info("writing nginx conf to "..nginx_file.canonical().to_string())
-				local conf = load_file(luanhost_file.."startup/nginx/nginx.ssl.conf.luan")
-				local nginx = ` conf(luanhost_dir_str,domain) `
-				nginx_file.write(nginx)
-			end
-		end
-	else -- http
-		if key_file.exists() or nginx_file.exists() then
-			changed = true
-			nginx_file.delete()
-			local_cer_file.delete()
-			local ptn = domain.."."
-			for _, file in ipairs(site_dir.children()) do
-				if starts_with(file.name(),ptn) then
-					file.delete()
-				end
-			end
-		end
-	end
-	if changed then
-		local cmd = [[
-sudo $(which nginx) -t -c "]]..luanhost_dir_str..[[/local/nginx.conf" && \
-sudo $(which nginx) -s reload -c "]]..luanhost_dir_str..[[/local/nginx.conf";
-]]
-		local s = uri("bash:"..cmd).read_text()
-		logger.info("reload_nginx "..s)
-	end
-	--logger.info "done"
-end
-Hosted.do_set_https = do_set_https	-- for testing
-
-function Hosted.set_https(is_https)
-	if Http.did_init() then
-		logger.error(new_error("set_https called outside of init.luan"))
-		return
-	end
-	local domain = Http.domain
-	local site_dir = uri("site:").parent()
-	local luanhost_dir = uri("file:.")
-
-	-- use for testing, so as to not hit rate limits
-	-- on the real letsencrypt servers
-	local dry_run = false
-
-	if not try_synchronized( function()
-		do_set_https(is_https,domain,site_dir,luanhost_dir,dry_run)
-	end, domain..".lock", 0 )() then
-		logger.info("set_https already running for "..domain..", skipping")
-	end
-end
-
-Hosted.set_https = Boot.no_security(Hosted.set_https)
--- a/src/luan/host/init.luan	Fri Dec 12 18:53:15 2025 -0800
+++ b/src/luan/host/init.luan	Sat Dec 13 20:25:56 2025 -0700
@@ -4,6 +4,7 @@
 local Package = require "luan:Package.luan"
 local Number = require "luan:Number.luan"
 local long = Number.long or error()
+local Boot = require "luan:Boot.luan"
 
 
 local dir, domain = ...
@@ -99,7 +100,8 @@
 end
 
 
-do_file "classpath:luan/host/https.luan"
+local Https = require "classpath:luan/host/Https.luan"
+Hosted.set_https = Boot.no_security(Https.set_https)
 
 
 local LuanJava = require "java:luan.Luan"