all repos

mugit @ da15464

🐮 git server that your cow will love

mugit/flake.nix (view raw)

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