|
1
|
{ |
|
2
|
description = "a git server that your cow will love"; |
|
3
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs"; |
|
4
|
outputs = { self, nixpkgs }: |
|
5
|
let |
|
6
|
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; |
|
7
|
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); |
|
8
|
in { |
|
9
|
packages = forAllSystems (pkgs: |
|
10
|
let version = self.rev or "dev"; |
|
11
|
in { |
|
12
|
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mugit; |
|
13
|
mugit = pkgs.buildGo126Module { |
|
14
|
pname = "mugit"; |
|
15
|
version = version; |
|
16
|
src = ./.; |
|
17
|
vendorHash = "sha256-eJ6L6o2cisJRZxoEDf9gtHL8T+xpnIDq9KPQr1vgLig="; |
|
18
|
ldflags = [ "-s" "-w" "-X main.version=${version}" ]; |
|
19
|
meta = with pkgs.lib; { |
|
20
|
homepage = "https://git.olexsmir.xyz/mugit"; |
|
21
|
license = licenses.mit; |
|
22
|
}; |
|
23
|
}; |
|
24
|
} |
|
25
|
); |
|
26
|
|
|
27
|
nixosModules.default = { config, lib, pkgs, ... }: |
|
28
|
with lib; |
|
29
|
let |
|
30
|
cfg = config.services.mugit; |
|
31
|
format = pkgs.formats.yaml { }; |
|
32
|
sshUser = if cfg.config.ssh.user != "" then cfg.config.ssh.user else cfg.user; |
|
33
|
configFile = if cfg.configFile != null |
|
34
|
then cfg.configFile |
|
35
|
else format.generate "config.yaml" cfg.config; |
|
36
|
in { |
|
37
|
options.services.mugit = { |
|
38
|
enable = mkEnableOption "mugit service"; |
|
39
|
|
|
40
|
package = mkOption { |
|
41
|
type = types.package; |
|
42
|
default = self.packages.${pkgs.stdenv.hostPlatform.system}.mugit; |
|
43
|
defaultText = literalExpression "self.packages.\${pkgs.stdenv.hostPlatform.system}.mugit"; |
|
44
|
description = "The mugit package to use."; |
|
45
|
}; |
|
46
|
|
|
47
|
exposeCli = mkOption { |
|
48
|
type = types.bool; |
|
49
|
default = false; |
|
50
|
description = "Whether to expose a mugit CLI wrapper to all system users, runs as the mugit user/group."; |
|
51
|
}; |
|
52
|
|
|
53
|
configFile = mkOption { |
|
54
|
type = types.nullOr types.path; |
|
55
|
default = null; |
|
56
|
description = "Path to an existing mugit configuration file. Mutually exclusive with `config`."; |
|
57
|
}; |
|
58
|
|
|
59
|
user = mkOption { |
|
60
|
type = types.str; |
|
61
|
default = "git"; |
|
62
|
description = "User account under which mugit runs."; |
|
63
|
}; |
|
64
|
|
|
65
|
group = mkOption { |
|
66
|
type = types.str; |
|
67
|
default = "git"; |
|
68
|
description = "Group under which mugit runs."; |
|
69
|
}; |
|
70
|
|
|
71
|
config = mkOption { |
|
72
|
default = {}; |
|
73
|
description = '' |
|
74
|
The primary mugit configuration. |
|
75
|
See [docs](https://github.com/olexsmir/mugit) for possible values. |
|
76
|
''; |
|
77
|
example = literalExpression '' |
|
78
|
{ |
|
79
|
meta.host = "git.example.org"; |
|
80
|
repo.dir = "/var/lib/mugit"; |
|
81
|
ssh = { |
|
82
|
enable = true; |
|
83
|
host_key = "/var/lib/mugit/key"; |
|
84
|
}; |
|
85
|
} |
|
86
|
''; |
|
87
|
type = types.submodule { |
|
88
|
options.meta = { |
|
89
|
title = mkOption { |
|
90
|
type = types.str; |
|
91
|
default = "mugit"; |
|
92
|
description = "Website title"; |
|
93
|
}; |
|
94
|
description = mkOption { |
|
95
|
type = types.str; |
|
96
|
default = ""; |
|
97
|
description = "Website description"; |
|
98
|
}; |
|
99
|
host = mkOption { |
|
100
|
type = types.str; |
|
101
|
default = ""; |
|
102
|
description = "Website CNAME (required)"; |
|
103
|
}; |
|
104
|
}; |
|
105
|
options.server = { |
|
106
|
host = mkOption { |
|
107
|
type = types.str; |
|
108
|
default = ""; |
|
109
|
description = "Host address"; |
|
110
|
}; |
|
111
|
port = mkOption { |
|
112
|
type = types.port; |
|
113
|
default = 8080; |
|
114
|
description = "Website port"; |
|
115
|
}; |
|
116
|
}; |
|
117
|
options.repo = { |
|
118
|
dir = mkOption { |
|
119
|
type = types.str; |
|
120
|
default = ""; |
|
121
|
description = "Directory which mugit will scan for repositories (required)"; |
|
122
|
}; |
|
123
|
masters = mkOption { |
|
124
|
type = types.listOf types.str; |
|
125
|
default = ["master" "main"]; |
|
126
|
description = "Master branch to look for"; |
|
127
|
}; |
|
128
|
readmes = mkOption { |
|
129
|
type = types.listOf types.str; |
|
130
|
default = ["README.md" "readme.md" "README.html" "readme.html" "README.txt" "readme.txt" "readme"]; |
|
131
|
description = "Readme files to look for"; |
|
132
|
}; |
|
133
|
}; |
|
134
|
options.ssh = { |
|
135
|
enable = mkOption { |
|
136
|
type = types.bool; |
|
137
|
default = false; |
|
138
|
description = "Whether to enable SSH git access"; |
|
139
|
}; |
|
140
|
user = mkOption { |
|
141
|
type = types.str; |
|
142
|
default = ""; |
|
143
|
description = "User used for git access. Defaults to the main user option."; |
|
144
|
}; |
|
145
|
host_key = mkOption { |
|
146
|
type = types.str; |
|
147
|
default = ""; |
|
148
|
description = "Path to ssh private key (required if ssh enabled)"; |
|
149
|
}; |
|
150
|
keys = mkOption { |
|
151
|
type = types.listOf types.str; |
|
152
|
default = []; |
|
153
|
description = "List of public ssh keys which are allows to do git pushes, and access private repositories"; |
|
154
|
}; |
|
155
|
}; |
|
156
|
options.mirror = { |
|
157
|
enable = mkOption { |
|
158
|
type = types.bool; |
|
159
|
default = false; |
|
160
|
description = "Wharever to run mirroring worker"; |
|
161
|
}; |
|
162
|
interval = mkOption { |
|
163
|
type = types.str; |
|
164
|
default = "8h"; |
|
165
|
description = "Interval in which mirroring will happen"; |
|
166
|
}; |
|
167
|
github_token = mkOption { |
|
168
|
type = types.str; |
|
169
|
default = ""; |
|
170
|
description = "Github token for pulling from github repos"; |
|
171
|
}; |
|
172
|
}; |
|
173
|
options.cache = { |
|
174
|
home_page = mkOption { |
|
175
|
type = types.str; |
|
176
|
default = "5m"; |
|
177
|
description = "For how long index page is cached"; |
|
178
|
}; |
|
179
|
readme = mkOption { |
|
180
|
type = types.str; |
|
181
|
default = "1m"; |
|
182
|
description = "For how long repos readme is cached"; |
|
183
|
}; |
|
184
|
}; |
|
185
|
}; |
|
186
|
}; |
|
187
|
}; |
|
188
|
|
|
189
|
config = mkIf cfg.enable { |
|
190
|
assertions = [ |
|
191
|
{ |
|
192
|
assertion = !cfg.config.ssh.enable || (cfg.config.ssh.keys != []); |
|
193
|
message = "SSH is enabled but no SSH keys provided. Please add keys to services.mugit.config.ssh.keys"; |
|
194
|
} |
|
195
|
]; |
|
196
|
|
|
197
|
users.groups.${cfg.group} = { }; |
|
198
|
users.users.${cfg.user} = { |
|
199
|
isSystemUser = true; |
|
200
|
useDefaultShell = true; |
|
201
|
group = cfg.group; |
|
202
|
home = cfg.config.repo.dir; |
|
203
|
createHome = true; |
|
204
|
description = "mugit service user"; |
|
205
|
}; |
|
206
|
|
|
207
|
services.openssh = mkIf cfg.config.ssh.enable { |
|
208
|
enable = true; |
|
209
|
extraConfig = '' |
|
210
|
Match User ${sshUser} |
|
211
|
AuthorizedKeysCommand /etc/ssh/mugit_authorized_keys |
|
212
|
AuthorizedKeysCommandUser ${sshUser} |
|
213
|
ChallengeResponseAuthentication no |
|
214
|
PasswordAuthentication no |
|
215
|
AllowUsers ${sshUser} |
|
216
|
''; |
|
217
|
}; |
|
218
|
|
|
219
|
environment.etc."ssh/mugit_authorized_keys" = mkIf cfg.config.ssh.enable { |
|
220
|
mode = "0555"; |
|
221
|
text = '' |
|
222
|
#!${pkgs.stdenv.shell} |
|
223
|
${cfg.package}/bin/mugit --config ${configFile} shell keys "$1" |
|
224
|
''; |
|
225
|
}; |
|
226
|
|
|
227
|
environment.systemPackages = lib.mkIf cfg.exposeCli [ |
|
228
|
(pkgs.runCommandLocal "mugit-completions" {} '' |
|
229
|
mkdir -p $out/share/bash-completion/completions |
|
230
|
mkdir -p $out/share/zsh/site-functions |
|
231
|
mkdir -p $out/share/fish/vendor_completions.d |
|
232
|
${cfg.package}/bin/mugit completion bash > $out/share/bash-completion/completions/mugit |
|
233
|
${cfg.package}/bin/mugit completion zsh > $out/share/zsh/site-functions/_mugit |
|
234
|
${cfg.package}/bin/mugit completion fish > $out/share/fish/vendor_completions.d/mugit.fish |
|
235
|
'') |
|
236
|
]; |
|
237
|
|
|
238
|
security.wrappers = lib.mkIf cfg.exposeCli { |
|
239
|
mugit = { |
|
240
|
source = |
|
241
|
let |
|
242
|
mugitWrapped = pkgs.writeScriptBin "mugit" '' |
|
243
|
#!${pkgs.bash}/bin/bash |
|
244
|
exec ${cfg.package}/bin/mugit --config ${configFile} "$@" |
|
245
|
''; |
|
246
|
in |
|
247
|
"${mugitWrapped}/bin/mugit"; |
|
248
|
owner = cfg.user; |
|
249
|
group = cfg.group; |
|
250
|
setuid = true; |
|
251
|
setgid = true; |
|
252
|
permissions = "u+rx,g+rx,o+rx"; |
|
253
|
}; |
|
254
|
}; |
|
255
|
|
|
256
|
systemd.services.mugit = { |
|
257
|
description = "mugit service"; |
|
258
|
wantedBy = [ "multi-user.target" ]; |
|
259
|
after = [ "network.target" ] ++ lib.optionals cfg.config.ssh.enable [ "sshd.service" ]; |
|
260
|
path = [ pkgs.git ]; |
|
261
|
serviceConfig = { |
|
262
|
Type = "simple"; |
|
263
|
User = cfg.user; |
|
264
|
Group = cfg.group; |
|
265
|
WorkingDirectory = cfg.config.repo.dir; |
|
266
|
StateDirectory = "mugit"; |
|
267
|
ExecStart = "${cfg.package}/bin/mugit serve --config ${configFile}"; |
|
268
|
Restart = "on-failure"; |
|
269
|
RestartSec = "5s"; |
|
270
|
NoNewPrivileges = true; |
|
271
|
PrivateTmp = true; |
|
272
|
ProtectSystem = "strict"; |
|
273
|
ProtectHome = true; |
|
274
|
ReadWritePaths = [ cfg.config.repo.dir ]; |
|
275
|
ProtectKernelTunables = true; |
|
276
|
ProtectKernelModules = true; |
|
277
|
ProtectControlGroups = true; |
|
278
|
}; |
|
279
|
}; |
|
280
|
}; |
|
281
|
}; |
|
282
|
}; |
|
283
|
} |