parent
f40699c907
commit
76d60b0fcd
@ -0,0 +1,117 @@ |
||||
{ config, lib, pkgs, ... }: |
||||
|
||||
with lib; |
||||
|
||||
let |
||||
cfg = config.services.molly-brown; |
||||
|
||||
settingsType = with types; |
||||
attrsOf (oneOf [ |
||||
int |
||||
str |
||||
(listOf str) |
||||
(attrsOf (oneOf [ int str (listOf str) (attrsOf str) ])) |
||||
]) // { |
||||
description = "primitive expression convertable to TOML"; |
||||
}; |
||||
|
||||
configFile = pkgs.runCommand "molly-brown.toml" { |
||||
buildInputs = [ pkgs.remarshal ]; |
||||
preferLocalBuild = true; |
||||
passAsFile = [ "settings" ]; |
||||
settings = builtins.toJSON cfg.settings; |
||||
} "remarshal -if json -of toml < $settingsPath > $out"; |
||||
in { |
||||
|
||||
options.services.molly-brown = { |
||||
|
||||
enable = mkEnableOption "Molly-Brown Gemini server"; |
||||
|
||||
port = mkOption { |
||||
default = 1965; |
||||
type = types.port; |
||||
description = '' |
||||
TCP port for molly-brown to bind to. |
||||
''; |
||||
}; |
||||
|
||||
hostName = mkOption { |
||||
type = types.str; |
||||
example = literalExample "config.networking.hostName"; |
||||
default = config.networking.hostName; |
||||
description = '' |
||||
The hostname to respond to requests for. Requests for URLs with |
||||
other hosts will result in a status 53 (PROXY REQUEST REFUSED) |
||||
response. |
||||
''; |
||||
}; |
||||
|
||||
certPath = mkOption { |
||||
type = types.path; |
||||
example = "/var/lib/acme/example.com/cert.pem"; |
||||
description = '' |
||||
Path to TLS certificate. An ACME certificate and key may be |
||||
shared with an HTTP server, but only if molly-brown has |
||||
permissions allowing it to read such keys. |
||||
|
||||
As an example: |
||||
<programlisting> |
||||
security.acme.certs."example.com".allowKeysForGroup = true; |
||||
systemd.services.molly-brown.serviceConfig.SupplementaryGroups = |
||||
[ config.security.acme.certs."example.com".group ]; |
||||
</programlisting> |
||||
''; |
||||
}; |
||||
|
||||
keyPath = mkOption { |
||||
type = types.path; |
||||
example = "/var/lib/acme/example.com/key.pem"; |
||||
description = "Path to TLS key. See <option>CertPath</option>."; |
||||
}; |
||||
|
||||
docBase = mkOption { |
||||
type = types.path; |
||||
example = "/var/lib/molly-brown"; |
||||
description = "Base directory for Gemini content."; |
||||
}; |
||||
|
||||
settings = mkOption { |
||||
type = settingsType; |
||||
default = { }; |
||||
description = '' |
||||
molly-brown configuration. Refer to |
||||
<link xlink:href="https://tildegit.org/solderpunk/molly-brown/src/branch/master/example.conf"/> |
||||
for details on supported values. |
||||
''; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
config = mkIf cfg.enable { |
||||
|
||||
services.molly-brown.settings = let logDir = "/var/log/molly-brown"; |
||||
in { |
||||
Port = cfg.port; |
||||
Hostname = cfg.hostName; |
||||
CertPath = cfg.certPath; |
||||
KeyPath = cfg.keyPath; |
||||
DocBase = cfg.docBase; |
||||
AccessLog = "${logDir}/access.log"; |
||||
ErrorLog = "${logDir}/error.log"; |
||||
}; |
||||
|
||||
systemd.services.molly-brown = { |
||||
description = "Molly Brown gemini server"; |
||||
after = [ "network.target" ]; |
||||
wantedBy = [ "multi-user.target" ]; |
||||
serviceConfig = { |
||||
DynamicUser = true; |
||||
LogsDirectory = "molly-brown"; |
||||
ExecStart = "${pkgs.molly-brown}/bin/molly-brown -c ${configFile}"; |
||||
Restart = "always"; |
||||
}; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
} |
@ -0,0 +1,71 @@ |
||||
import ./make-test-python.nix ({ pkgs, ... }: |
||||
|
||||
let testString = "NixOS Gemini test successful"; |
||||
in { |
||||
|
||||
name = "molly-brown"; |
||||
meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ ehmry ]; }; |
||||
|
||||
nodes = { |
||||
|
||||
geminiServer = { config, pkgs, ... }: |
||||
let |
||||
inherit (config.networking) hostName; |
||||
cfg = config.services.molly-brown; |
||||
in { |
||||
|
||||
environment.systemPackages = [ |
||||
(pkgs.writeScriptBin "test-gemini" '' |
||||
#!${pkgs.python3}/bin/python |
||||
|
||||
import socket |
||||
import ssl |
||||
import tempfile |
||||
import textwrap |
||||
import urllib.parse |
||||
|
||||
url = "gemini://geminiServer/init.gmi" |
||||
parsed_url = urllib.parse.urlparse(url) |
||||
|
||||
s = socket.create_connection((parsed_url.netloc, 1965)) |
||||
context = ssl.SSLContext() |
||||
context.check_hostname = False |
||||
context.verify_mode = ssl.CERT_NONE |
||||
s = context.wrap_socket(s, server_hostname=parsed_url.netloc) |
||||
s.sendall((url + "\r\n").encode("UTF-8")) |
||||
fp = s.makefile("rb") |
||||
print(fp.readline().strip()) |
||||
print(fp.readline().strip()) |
||||
print(fp.readline().strip()) |
||||
'') |
||||
]; |
||||
|
||||
networking.firewall.allowedTCPPorts = [ cfg.settings.Port ]; |
||||
|
||||
services.molly-brown = { |
||||
enable = true; |
||||
docBase = "/tmp/docs"; |
||||
certPath = "/tmp/cert.pem"; |
||||
keyPath = "/tmp/key.pem"; |
||||
}; |
||||
|
||||
systemd.services.molly-brown.preStart = '' |
||||
${pkgs.openssl}/bin/openssl genrsa -out "/tmp/key.pem" |
||||
${pkgs.openssl}/bin/openssl req -new \ |
||||
-subj "/CN=${config.networking.hostName}" \ |
||||
-key "/tmp/key.pem" -out /tmp/request.pem |
||||
${pkgs.openssl}/bin/openssl x509 -req -days 3650 \ |
||||
-in /tmp/request.pem -signkey "/tmp/key.pem" -out "/tmp/cert.pem" |
||||
|
||||
mkdir -p "${cfg.settings.DocBase}" |
||||
echo "${testString}" > "${cfg.settings.DocBase}/test.gmi" |
||||
''; |
||||
}; |
||||
}; |
||||
testScript = '' |
||||
geminiServer.wait_for_unit("molly-brown") |
||||
geminiServer.wait_for_open_port(1965) |
||||
geminiServer.succeed("test-gemini") |
||||
''; |
||||
|
||||
}) |
Loading…
Reference in new issue