Skip to content

Commit cc594f9

Browse files
authored
nixos/h2o: module init (NixOS#382527)
2 parents 51236d9 + 2bcb696 commit cc594f9

File tree

9 files changed

+639
-1
lines changed

9 files changed

+639
-1
lines changed

nixos/modules/module-list.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,7 @@
16081608
./services/web-servers/darkhttpd.nix
16091609
./services/web-servers/fcgiwrap.nix
16101610
./services/web-servers/garage.nix
1611+
./services/web-servers/h2o/default.nix
16111612
./services/web-servers/hitch/default.nix
16121613
./services/web-servers/jboss/default.nix
16131614
./services/web-servers/keter
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
7+
8+
# TODO: ACME
9+
# TODO: Gems includes for Mruby
10+
# TODO: Recommended options
11+
let
12+
cfg = config.services.h2o;
13+
14+
inherit (lib)
15+
literalExpression
16+
mkDefault
17+
mkEnableOption
18+
mkIf
19+
mkOption
20+
types
21+
;
22+
23+
settingsFormat = pkgs.formats.yaml { };
24+
25+
hostsConfig = lib.concatMapAttrs (
26+
name: value:
27+
let
28+
port = {
29+
HTTP = lib.attrByPath [ "http" "port" ] cfg.defaultHTTPListenPort value;
30+
TLS = lib.attrByPath [ "tls" "port" ] cfg.defaultTLSListenPort value;
31+
};
32+
serverName = if value.serverName != null then value.serverName else name;
33+
in
34+
# HTTP settings
35+
lib.optionalAttrs (value.tls == null || value.tls.policy == "add") {
36+
"${serverName}:${builtins.toString port.HTTP}" = value.settings // {
37+
listen.port = port.HTTP;
38+
};
39+
}
40+
# Redirect settings
41+
// lib.optionalAttrs (value.tls != null && value.tls.policy == "force") {
42+
"${serverName}:${builtins.toString port.HTTP}" = {
43+
listen.port = port.HTTP;
44+
paths."/" = {
45+
redirect = {
46+
status = value.tls.redirectCode;
47+
url = "https://${serverName}:${builtins.toString port.TLS}";
48+
};
49+
};
50+
};
51+
}
52+
# TLS settings
53+
//
54+
lib.optionalAttrs
55+
(
56+
value.tls != null
57+
&& builtins.elem value.tls.policy [
58+
"add"
59+
"only"
60+
"force"
61+
]
62+
)
63+
{
64+
"${serverName}:${builtins.toString port.TLS}" = value.settings // {
65+
listen =
66+
let
67+
identity = value.tls.identity;
68+
in
69+
{
70+
port = port.TLS;
71+
ssl = value.tls.extraSettings or { } // {
72+
inherit identity;
73+
};
74+
};
75+
};
76+
}
77+
) cfg.hosts;
78+
79+
h2oConfig = settingsFormat.generate "h2o.yaml" (
80+
lib.recursiveUpdate { hosts = hostsConfig; } cfg.settings
81+
);
82+
in
83+
{
84+
options = {
85+
services.h2o = {
86+
enable = mkEnableOption "H2O web server";
87+
88+
user = mkOption {
89+
type = types.nonEmptyStr;
90+
default = "h2o";
91+
description = "User running H2O service";
92+
};
93+
94+
group = mkOption {
95+
type = types.nonEmptyStr;
96+
default = "h2o";
97+
description = "Group running H2O services";
98+
};
99+
100+
package = lib.mkPackageOption pkgs "h2o" {
101+
example = ''
102+
pkgs.h2o.override {
103+
withMruby = true;
104+
};
105+
'';
106+
};
107+
108+
defaultHTTPListenPort = mkOption {
109+
type = types.port;
110+
default = 80;
111+
description = ''
112+
If hosts do not specify listen.port, use these ports for HTTP by default.
113+
'';
114+
example = 8080;
115+
};
116+
117+
defaultTLSListenPort = mkOption {
118+
type = types.port;
119+
default = 443;
120+
description = ''
121+
If hosts do not specify listen.port, use these ports for SSL by default.
122+
'';
123+
example = 8443;
124+
};
125+
126+
mode = mkOption {
127+
type =
128+
with types;
129+
nullOr (enum [
130+
"daemon"
131+
"master"
132+
"worker"
133+
"test"
134+
]);
135+
default = "master";
136+
description = "Operating mode of H2O";
137+
};
138+
139+
settings = mkOption {
140+
type = settingsFormat.type;
141+
description = "Configuration for H2O (see <https://h2o.examp1e.net/configure.html>)";
142+
};
143+
144+
hosts = mkOption {
145+
type = types.attrsOf (
146+
types.submodule (
147+
import ./vhost-options.nix {
148+
inherit config lib;
149+
}
150+
)
151+
);
152+
default = { };
153+
description = ''
154+
The `hosts` config to be merged with the settings.
155+
156+
Note that unlike YAML used for H2O, Nix will not support duplicate
157+
keys to, for instance, have multiple listens in a host block; use the
158+
virtual host options in like `http` & `tls` or use `$HOST:$PORT`
159+
keys if manually specifying config.
160+
'';
161+
example =
162+
literalExpression
163+
# nix
164+
''
165+
{
166+
"hydra.example.com" = {
167+
tls = {
168+
policy = "force";
169+
indentity = [
170+
{
171+
key-file = "/path/to/key";
172+
certificate-file = "/path/to/cert";
173+
};
174+
];
175+
extraSettings = {
176+
minimum-version = "TLSv1.3";
177+
};
178+
};
179+
settings = {
180+
paths."/" = {
181+
"file:dir" = "/var/www/default";
182+
};
183+
};
184+
};
185+
}
186+
'';
187+
};
188+
};
189+
};
190+
191+
config = mkIf cfg.enable {
192+
users = {
193+
users.${cfg.user} =
194+
{
195+
group = cfg.group;
196+
}
197+
// lib.optionalAttrs (cfg.user == "h2o") {
198+
isSystemUser = true;
199+
};
200+
groups.${cfg.group} = { };
201+
};
202+
203+
systemd.services.h2o = {
204+
description = "H2O web server service";
205+
wantedBy = [ "multi-user.target" ];
206+
after = [ "network.target" ];
207+
208+
serviceConfig = {
209+
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
210+
ExecStop = "${pkgs.coreutils}/bin/kill -s QUIT $MAINPID";
211+
User = cfg.user;
212+
Restart = "always";
213+
RestartSec = "10s";
214+
RuntimeDirectory = "h2o";
215+
RuntimeDirectoryMode = "0750";
216+
CacheDirectory = "h2o";
217+
CacheDirectoryMode = "0750";
218+
LogsDirectory = "h2o";
219+
LogsDirectoryMode = "0750";
220+
ProtectSystem = "strict";
221+
ProtectHome = mkDefault true;
222+
PrivateTmp = true;
223+
PrivateDevices = true;
224+
ProtectHostname = true;
225+
ProtectClock = true;
226+
ProtectKernelTunables = true;
227+
ProtectKernelModules = true;
228+
ProtectKernelLogs = true;
229+
ProtectControlGroups = true;
230+
RestrictAddressFamilies = [
231+
"AF_UNIX"
232+
"AF_INET"
233+
"AF_INET6"
234+
];
235+
RestrictNamespaces = true;
236+
LockPersonality = true;
237+
RestrictRealtime = true;
238+
RestrictSUIDSGID = true;
239+
RemoveIPC = true;
240+
PrivateMounts = true;
241+
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
242+
CapabilitiesBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
243+
};
244+
245+
script =
246+
let
247+
args =
248+
[
249+
"--conf"
250+
"${h2oConfig}"
251+
]
252+
++ lib.optionals (cfg.mode != null) [
253+
"--mode"
254+
cfg.mode
255+
];
256+
in
257+
''
258+
${lib.getExe cfg.package} ${lib.strings.escapeShellArgs args}
259+
'';
260+
};
261+
};
262+
263+
}

0 commit comments

Comments
 (0)