1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.athens;
9
10 athensConfig = lib.flip lib.recursiveUpdate cfg.extraConfig ({
11 GoBinary = "${cfg.goBinary}/bin/go";
12 GoEnv = cfg.goEnv;
13 GoBinaryEnvVars = lib.mapAttrsToList (k: v: "${k}=${v}") cfg.goBinaryEnvVars;
14 GoGetWorkers = cfg.goGetWorkers;
15 GoGetDir = cfg.goGetDir;
16 ProtocolWorkers = cfg.protocolWorkers;
17 LogLevel = cfg.logLevel;
18 CloudRuntime = cfg.cloudRuntime;
19 EnablePprof = cfg.enablePprof;
20 PprofPort = ":${toString cfg.pprofPort}";
21 FilterFile = cfg.filterFile;
22 RobotsFile = cfg.robotsFile;
23 Timeout = cfg.timeout;
24 StorageType = cfg.storageType;
25 TLSCertFile = cfg.tlsCertFile;
26 TLSKeyFile = cfg.tlsKeyFile;
27 Port = ":${toString cfg.port}";
28 UnixSocket = cfg.unixSocket;
29 GlobalEndpoint = cfg.globalEndpoint;
30 BasicAuthUser = cfg.basicAuthUser;
31 BasicAuthPass = cfg.basicAuthPass;
32 ForceSSL = cfg.forceSSL;
33 ValidatorHook = cfg.validatorHook;
34 PathPrefix = cfg.pathPrefix;
35 NETRCPath = cfg.netrcPath;
36 GithubToken = cfg.githubToken;
37 HGRCPath = cfg.hgrcPath;
38 TraceExporter = cfg.traceExporter;
39 StatsExporter = cfg.statsExporter;
40 SumDBs = cfg.sumDBs;
41 NoSumPatterns = cfg.noSumPatterns;
42 DownloadMode = cfg.downloadMode;
43 NetworkMode = cfg.networkMode;
44 DownloadURL = cfg.downloadURL;
45 SingleFlightType = cfg.singleFlightType;
46 IndexType = cfg.indexType;
47 ShutdownTimeout = cfg.shutdownTimeout;
48 SingleFlight = {
49 Etcd = {
50 Endpoints = builtins.concatStringsSep "," cfg.singleFlight.etcd.endpoints;
51 };
52 Redis = {
53 Endpoint = cfg.singleFlight.redis.endpoint;
54 Password = cfg.singleFlight.redis.password;
55 LockConfig = {
56 TTL = cfg.singleFlight.redis.lockConfig.ttl;
57 Timeout = cfg.singleFlight.redis.lockConfig.timeout;
58 MaxRetries = cfg.singleFlight.redis.lockConfig.maxRetries;
59 };
60 };
61 RedisSentinel = {
62 Endpoints = cfg.singleFlight.redisSentinel.endpoints;
63 MasterName = cfg.singleFlight.redisSentinel.masterName;
64 SentinelPassword = cfg.singleFlight.redisSentinel.sentinelPassword;
65 LockConfig = {
66 TTL = cfg.singleFlight.redisSentinel.lockConfig.ttl;
67 Timeout = cfg.singleFlight.redisSentinel.lockConfig.timeout;
68 MaxRetries = cfg.singleFlight.redisSentinel.lockConfig.maxRetries;
69 };
70 };
71 };
72 Storage = {
73 CDN = {
74 Endpoint = cfg.storage.cdn.endpoint;
75 };
76 Disk = {
77 RootPath = cfg.storage.disk.rootPath;
78 };
79 GCP = {
80 ProjectID = cfg.storage.gcp.projectID;
81 Bucket = cfg.storage.gcp.bucket;
82 JSONKey = cfg.storage.gcp.jsonKey;
83 };
84 Minio = {
85 Endpoint = cfg.storage.minio.endpoint;
86 Key = cfg.storage.minio.key;
87 Secret = cfg.storage.minio.secret;
88 EnableSSL = cfg.storage.minio.enableSSL;
89 Bucket = cfg.storage.minio.bucket;
90 region = cfg.storage.minio.region;
91 };
92 Mongo = {
93 URL = cfg.storage.mongo.url;
94 DefaultDBName = cfg.storage.mongo.defaultDBName;
95 CertPath = cfg.storage.mongo.certPath;
96 Insecure = cfg.storage.mongo.insecure;
97 };
98 S3 = {
99 Region = cfg.storage.s3.region;
100 Key = cfg.storage.s3.key;
101 Secret = cfg.storage.s3.secret;
102 Token = cfg.storage.s3.token;
103 Bucket = cfg.storage.s3.bucket;
104 ForcePathStyle = cfg.storage.s3.forcePathStyle;
105 UseDefaultConfiguration = cfg.storage.s3.useDefaultConfiguration;
106 CredentialsEndpoint = cfg.storage.s3.credentialsEndpoint;
107 AwsContainerCredentialsRelativeURI = cfg.storage.s3.awsContainerCredentialsRelativeURI;
108 Endpoint = cfg.storage.s3.endpoint;
109 };
110 AzureBlob = {
111 AccountName = cfg.storage.azureblob.accountName;
112 AccountKey = cfg.storage.azureblob.accountKey;
113 ContainerName = cfg.storage.azureblob.containerName;
114 };
115 External = {
116 URL = cfg.storage.external.url;
117 };
118 };
119 Index = {
120 MySQL = {
121 Protocol = cfg.index.mysql.protocol;
122 Host = cfg.index.mysql.host;
123 Port = cfg.index.mysql.port;
124 User = cfg.index.mysql.user;
125 Password = cfg.index.mysql.password;
126 Database = cfg.index.mysql.database;
127 Params = {
128 parseTime = cfg.index.mysql.params.parseTime;
129 timeout = cfg.index.mysql.params.timeout;
130 };
131 };
132 Postgres = {
133 Host = cfg.index.postgres.host;
134 Port = cfg.index.postgres.port;
135 User = cfg.index.postgres.user;
136 Password = cfg.index.postgres.password;
137 Database = cfg.index.postgres.database;
138 Params = {
139 connect_timeout = cfg.index.postgres.params.connect_timeout;
140 sslmode = cfg.index.postgres.params.sslmode;
141 };
142 };
143 };
144 });
145
146 configFile = lib.pipe athensConfig [
147 (lib.filterAttrsRecursive (_k: v: v != null))
148 ((pkgs.formats.toml { }).generate "config.toml")
149 ];
150in
151{
152 meta = {
153 maintainers = pkgs.athens.meta.maintainers;
154 doc = ./athens.md;
155 };
156
157 options.services.athens = {
158 enable = lib.mkEnableOption "Go module datastore and proxy";
159
160 package = lib.mkOption {
161 default = pkgs.athens;
162 defaultText = lib.literalExpression "pkgs.athens";
163 example = "pkgs.athens";
164 description = "Which athens derivation to use";
165 type = lib.types.package;
166 };
167
168 goBinary = lib.mkOption {
169 type = lib.types.package;
170 default = pkgs.go;
171 defaultText = lib.literalExpression "pkgs.go";
172 example = "pkgs.go_1_23";
173 description = ''
174 The Go package used by Athens at runtime.
175
176 Athens primarily runs two Go commands:
177 1. `go mod download -json <module>@<version>`
178 2. `go list -m -json <module>@latest`
179 '';
180 };
181
182 goEnv = lib.mkOption {
183 type = lib.types.enum [
184 "development"
185 "production"
186 ];
187 description = "Specifies the type of environment to run. One of 'development' or 'production'.";
188 default = "development";
189 example = "production";
190 };
191
192 goBinaryEnvVars = lib.mkOption {
193 type = lib.types.attrs;
194 description = "Environment variables to pass to the Go binary.";
195 example = ''
196 { "GOPROXY" = "direct", "GODEBUG" = "true" }
197 '';
198 default = { };
199 };
200
201 goGetWorkers = lib.mkOption {
202 type = lib.types.int;
203 description = "Number of workers concurrently downloading modules.";
204 default = 10;
205 example = 32;
206 };
207
208 goGetDir = lib.mkOption {
209 type = lib.types.nullOr lib.types.path;
210 description = ''
211 Temporary directory that Athens will use to
212 fetch modules from VCS prior to persisting
213 them to a storage backend.
214
215 If the value is empty, Athens will use the
216 default OS temp directory.
217 '';
218 default = null;
219 example = "/tmp/athens";
220 };
221
222 protocolWorkers = lib.mkOption {
223 type = lib.types.int;
224 description = "Number of workers concurrently serving protocol paths.";
225 default = 30;
226 };
227
228 logLevel = lib.mkOption {
229 type = lib.types.nullOr (
230 lib.types.enum [
231 "panic"
232 "fatal"
233 "error"
234 "warning"
235 "info"
236 "debug"
237 "trace"
238 ]
239 );
240 description = ''
241 Log level for Athens.
242 Supports all logrus log levels (https://github.com/Sirupsen/logrus#level-logging)".
243 '';
244 default = "warning";
245 example = "debug";
246 };
247
248 cloudRuntime = lib.mkOption {
249 type = lib.types.enum [
250 "GCP"
251 "none"
252 ];
253 description = ''
254 Specifies the Cloud Provider on which the Proxy/registry is running.
255 '';
256 default = "none";
257 example = "GCP";
258 };
259
260 enablePprof = lib.mkOption {
261 type = lib.types.bool;
262 description = "Enable pprof endpoints.";
263 default = false;
264 };
265
266 pprofPort = lib.mkOption {
267 type = lib.types.port;
268 description = "Port number for pprof endpoints.";
269 default = 3301;
270 example = 443;
271 };
272
273 filterFile = lib.mkOption {
274 type = lib.types.nullOr lib.types.path;
275 description = ''Filename for the include exclude filter.'';
276 default = null;
277 example = lib.literalExpression ''
278 pkgs.writeText "filterFile" '''
279 - github.com/azure
280 + github.com/azure/azure-sdk-for-go
281 D golang.org/x/tools
282 '''
283 '';
284 };
285
286 robotsFile = lib.mkOption {
287 type = lib.types.nullOr lib.types.path;
288 description = ''Provides /robots.txt for net crawlers.'';
289 default = null;
290 example = lib.literalExpression ''pkgs.writeText "robots.txt" "# my custom robots.txt ..."'';
291 };
292
293 timeout = lib.mkOption {
294 type = lib.types.int;
295 description = "Timeout for external network calls in seconds.";
296 default = 300;
297 example = 3;
298 };
299
300 storageType = lib.mkOption {
301 type = lib.types.enum [
302 "memory"
303 "disk"
304 "mongo"
305 "gcp"
306 "minio"
307 "s3"
308 "azureblob"
309 "external"
310 ];
311 description = "Specifies the type of storage backend to use.";
312 default = "disk";
313 };
314
315 tlsCertFile = lib.mkOption {
316 type = lib.types.nullOr lib.types.path;
317 description = "Path to the TLS certificate file.";
318 default = null;
319 example = "/etc/ssl/certs/athens.crt";
320 };
321
322 tlsKeyFile = lib.mkOption {
323 type = lib.types.nullOr lib.types.path;
324 description = "Path to the TLS key file.";
325 default = null;
326 example = "/etc/ssl/certs/athens.key";
327 };
328
329 port = lib.mkOption {
330 type = lib.types.port;
331 default = 3000;
332 description = ''
333 Port number Athens listens on.
334 '';
335 example = 443;
336 };
337
338 unixSocket = lib.mkOption {
339 type = lib.types.nullOr lib.types.path;
340 description = ''
341 Path to the unix socket file.
342 If set, Athens will listen on the unix socket instead of TCP socket.
343 '';
344 default = null;
345 example = "/run/athens.sock";
346 };
347
348 globalEndpoint = lib.mkOption {
349 type = lib.types.str;
350 description = ''
351 Endpoint for a package registry in case of a proxy cache miss.
352 '';
353 default = "";
354 example = "http://upstream-athens.example.com:3000";
355 };
356
357 basicAuthUser = lib.mkOption {
358 type = lib.types.nullOr lib.types.str;
359 description = ''
360 Username for basic auth.
361 '';
362 default = null;
363 example = "user";
364 };
365
366 basicAuthPass = lib.mkOption {
367 type = lib.types.nullOr lib.types.str;
368 description = ''
369 Password for basic auth. Warning: this is stored in plain text in the config file.
370 '';
371 default = null;
372 example = "swordfish";
373 };
374
375 forceSSL = lib.mkOption {
376 type = lib.types.bool;
377 description = ''
378 Force SSL redirects for incoming requests.
379 '';
380 default = false;
381 };
382
383 validatorHook = lib.mkOption {
384 type = lib.types.nullOr lib.types.str;
385 description = ''
386 Endpoint to validate modules against.
387
388 Not used if empty.
389 '';
390 default = null;
391 example = "https://validation.example.com";
392 };
393
394 pathPrefix = lib.mkOption {
395 type = lib.types.nullOr lib.types.str;
396 description = ''
397 Sets basepath for all routes.
398 '';
399 default = null;
400 example = "/athens";
401 };
402
403 netrcPath = lib.mkOption {
404 type = lib.types.nullOr lib.types.path;
405 description = ''
406 Path to the .netrc file.
407 '';
408 default = null;
409 example = "/home/user/.netrc";
410 };
411
412 githubToken = lib.mkOption {
413 type = lib.types.nullOr lib.types.str;
414 description = ''
415 Creates .netrc file with the given token to be used for GitHub.
416 Warning: this is stored in plain text in the config file.
417 '';
418 default = null;
419 example = "ghp_1234567890";
420 };
421
422 hgrcPath = lib.mkOption {
423 type = lib.types.nullOr lib.types.path;
424 description = ''
425 Path to the .hgrc file.
426 '';
427 default = null;
428 example = "/home/user/.hgrc";
429 };
430
431 traceExporter = lib.mkOption {
432 type = lib.types.nullOr (
433 lib.types.enum [
434 "jaeger"
435 "datadog"
436 ]
437 );
438 description = ''
439 Trace exporter to use.
440 '';
441 default = null;
442 };
443
444 traceExporterURL = lib.mkOption {
445 type = lib.types.nullOr lib.types.str;
446 description = ''
447 URL endpoint that traces will be sent to.
448 '';
449 default = null;
450 example = "http://localhost:14268";
451 };
452
453 statsExporter = lib.mkOption {
454 type = lib.types.nullOr (lib.types.enum [ "prometheus" ]);
455 description = "Stats exporter to use.";
456 default = null;
457 };
458
459 sumDBs = lib.mkOption {
460 type = lib.types.listOf lib.types.str;
461 description = ''
462 List of fully qualified URLs that Athens will proxy
463 that the go command can use a checksum verifier.
464 '';
465 default = [ "https://sum.golang.org" ];
466 };
467
468 noSumPatterns = lib.mkOption {
469 type = lib.types.listOf lib.types.str;
470 description = ''
471 List of patterns that Athens sum db proxy will return a 403 for.
472 '';
473 default = [ ];
474 example = [ "github.com/mycompany/*" ];
475 };
476
477 downloadMode = lib.mkOption {
478 type = lib.types.oneOf [
479 (lib.types.enum [
480 "sync"
481 "async"
482 "redirect"
483 "async_redirect"
484 "none"
485 ])
486 (lib.types.strMatching "^file:.*$|^custom:.*$")
487 ];
488 description = ''
489 Defines how Athens behaves when a module@version
490 is not found in storage. There are 7 options:
491 1. "sync": download the module synchronously and
492 return the results to the client.
493 2. "async": return 404, but asynchronously store the module
494 in the storage backend.
495 3. "redirect": return a 301 redirect status to the client
496 with the base URL as the DownloadRedirectURL from below.
497 4. "async_redirect": same as option number 3 but it will
498 asynchronously store the module to the backend.
499 5. "none": return 404 if a module is not found and do nothing.
500 6. "file:<path>": will point to an HCL file that specifies
501 any of the 5 options above based on different import paths.
502 7. "custom:<base64-encoded-hcl>" is the same as option 6
503 but the file is fully encoded in the option. This is
504 useful for using an environment variable in serverless
505 deployments.
506 '';
507 default = "async_redirect";
508 };
509
510 networkMode = lib.mkOption {
511 type = lib.types.enum [
512 "strict"
513 "offline"
514 "fallback"
515 ];
516 description = ''
517 Configures how Athens will return the results
518 of the /list endpoint as it can be assembled from both its own
519 storage and the upstream VCS.
520
521 Note, that for better error messaging, this would also affect how other
522 endpoints behave.
523
524 Modes:
525 1. strict: merge VCS versions with storage versions, but fail if either of them fails.
526 2. offline: only get storage versions, never reach out to VCS.
527 3. fallback: only return storage versions, if VCS fails. Note this means that you may
528 see inconsistent results since fallback mode does a best effort of giving you what's
529 available at the time of requesting versions.
530 '';
531 default = "strict";
532 };
533
534 downloadURL = lib.mkOption {
535 type = lib.types.str;
536 description = "URL used if DownloadMode is set to redirect.";
537 default = "https://proxy.golang.org";
538 };
539
540 singleFlightType = lib.mkOption {
541 type = lib.types.enum [
542 "memory"
543 "etcd"
544 "redis"
545 "redis-sentinel"
546 "gcp"
547 "azureblob"
548 ];
549 description = ''
550 Determines what mechanism Athens uses to manage concurrency flowing into the Athens backend.
551 '';
552 default = "memory";
553 };
554
555 indexType = lib.mkOption {
556 type = lib.types.enum [
557 "none"
558 "memory"
559 "mysql"
560 "postgres"
561 ];
562 description = ''
563 Type of index backend Athens will use.
564 '';
565 default = "none";
566 };
567
568 shutdownTimeout = lib.mkOption {
569 type = lib.types.int;
570 description = ''
571 Number of seconds to wait for the server to shutdown gracefully.
572 '';
573 default = 60;
574 example = 1;
575 };
576
577 singleFlight = {
578 etcd = {
579 endpoints = lib.mkOption {
580 type = lib.types.listOf lib.types.str;
581 description = "URLs that determine all distributed etcd servers.";
582 default = [ ];
583 example = [ "localhost:2379" ];
584 };
585 };
586 redis = {
587 endpoint = lib.mkOption {
588 type = lib.types.str;
589 description = "URL of the redis server.";
590 default = "";
591 example = "localhost:6379";
592 };
593 password = lib.mkOption {
594 type = lib.types.str;
595 description = "Password for the redis server. Warning: this is stored in plain text in the config file.";
596 default = "";
597 example = "swordfish";
598 };
599
600 lockConfig = {
601 ttl = lib.mkOption {
602 type = lib.types.int;
603 description = "TTL for the lock in seconds.";
604 default = 900;
605 example = 1;
606 };
607 timeout = lib.mkOption {
608 type = lib.types.int;
609 description = "Timeout for the lock in seconds.";
610 default = 15;
611 example = 1;
612 };
613 maxRetries = lib.mkOption {
614 type = lib.types.int;
615 description = "Maximum number of retries for the lock.";
616 default = 10;
617 example = 1;
618 };
619 };
620 };
621
622 redisSentinel = {
623 endpoints = lib.mkOption {
624 type = lib.types.listOf lib.types.str;
625 description = "URLs that determine all distributed redis servers.";
626 default = [ ];
627 example = [ "localhost:26379" ];
628 };
629 masterName = lib.mkOption {
630 type = lib.types.str;
631 description = "Name of the sentinel master server.";
632 default = "";
633 example = "redis-1";
634 };
635 sentinelPassword = lib.mkOption {
636 type = lib.types.str;
637 description = "Password for the sentinel server. Warning: this is stored in plain text in the config file.";
638 default = "";
639 example = "swordfish";
640 };
641
642 lockConfig = {
643 ttl = lib.mkOption {
644 type = lib.types.int;
645 description = "TTL for the lock in seconds.";
646 default = 900;
647 example = 1;
648 };
649 timeout = lib.mkOption {
650 type = lib.types.int;
651 description = "Timeout for the lock in seconds.";
652 default = 15;
653 example = 1;
654 };
655 maxRetries = lib.mkOption {
656 type = lib.types.int;
657 description = "Maximum number of retries for the lock.";
658 default = 10;
659 example = 1;
660 };
661 };
662 };
663 };
664
665 storage = {
666 cdn = {
667 endpoint = lib.mkOption {
668 type = lib.types.nullOr lib.types.str;
669 description = "hostname of the CDN server.";
670 example = "cdn.example.com";
671 default = null;
672 };
673 };
674
675 disk = {
676 rootPath = lib.mkOption {
677 type = lib.types.nullOr lib.types.path;
678 description = "Athens disk root folder.";
679 default = "/var/lib/athens";
680 };
681 };
682
683 gcp = {
684 projectID = lib.mkOption {
685 type = lib.types.nullOr lib.types.str;
686 description = "GCP project ID.";
687 example = "my-project";
688 default = null;
689 };
690 bucket = lib.mkOption {
691 type = lib.types.nullOr lib.types.str;
692 description = "GCP backend storage bucket.";
693 example = "my-bucket";
694 default = null;
695 };
696 jsonKey = lib.mkOption {
697 type = lib.types.nullOr lib.types.str;
698 description = "Base64 encoded GCP service account key. Warning: this is stored in plain text in the config file.";
699 default = null;
700 };
701 };
702
703 minio = {
704 endpoint = lib.mkOption {
705 type = lib.types.nullOr lib.types.str;
706 description = "Endpoint of the minio storage backend.";
707 example = "minio.example.com:9001";
708 default = null;
709 };
710 key = lib.mkOption {
711 type = lib.types.nullOr lib.types.str;
712 description = "Access key id for the minio storage backend.";
713 example = "minio";
714 default = null;
715 };
716 secret = lib.mkOption {
717 type = lib.types.nullOr lib.types.str;
718 description = "Secret key for the minio storage backend. Warning: this is stored in plain text in the config file.";
719 example = "minio123";
720 default = null;
721 };
722 enableSSL = lib.mkOption {
723 type = lib.types.bool;
724 description = "Enable SSL for the minio storage backend.";
725 default = false;
726 };
727 bucket = lib.mkOption {
728 type = lib.types.nullOr lib.types.str;
729 description = "Bucket name for the minio storage backend.";
730 example = "gomods";
731 default = null;
732 };
733 region = lib.mkOption {
734 type = lib.types.nullOr lib.types.str;
735 description = "Region for the minio storage backend.";
736 example = "us-east-1";
737 default = null;
738 };
739 };
740
741 mongo = {
742 url = lib.mkOption {
743 type = lib.types.nullOr lib.types.str;
744 description = "URL of the mongo database.";
745 example = "mongodb://localhost:27017";
746 default = null;
747 };
748 defaultDBName = lib.mkOption {
749 type = lib.types.nullOr lib.types.str;
750 description = "Name of the mongo database.";
751 example = "athens";
752 default = null;
753 };
754 certPath = lib.mkOption {
755 type = lib.types.nullOr lib.types.path;
756 description = "Path to the certificate file for the mongo database.";
757 example = "/etc/ssl/mongo.pem";
758 default = null;
759 };
760 insecure = lib.mkOption {
761 type = lib.types.bool;
762 description = "Allow insecure connections to the mongo database.";
763 default = false;
764 };
765 };
766
767 s3 = {
768 region = lib.mkOption {
769 type = lib.types.nullOr lib.types.str;
770 description = "Region of the S3 storage backend.";
771 example = "eu-west-3";
772 default = null;
773 };
774 key = lib.mkOption {
775 type = lib.types.nullOr lib.types.str;
776 description = "Access key id for the S3 storage backend.";
777 example = "minio";
778 default = null;
779 };
780 secret = lib.mkOption {
781 type = lib.types.str;
782 description = "Secret key for the S3 storage backend. Warning: this is stored in plain text in the config file.";
783 default = "";
784 };
785 token = lib.mkOption {
786 type = lib.types.nullOr lib.types.str;
787 description = "Token for the S3 storage backend. Warning: this is stored in plain text in the config file.";
788 default = null;
789 };
790 bucket = lib.mkOption {
791 type = lib.types.nullOr lib.types.str;
792 description = "Bucket name for the S3 storage backend.";
793 example = "gomods";
794 default = null;
795 };
796 forcePathStyle = lib.mkOption {
797 type = lib.types.bool;
798 description = "Force path style for the S3 storage backend.";
799 default = false;
800 };
801 useDefaultConfiguration = lib.mkOption {
802 type = lib.types.bool;
803 description = "Use default configuration for the S3 storage backend.";
804 default = false;
805 };
806 credentialsEndpoint = lib.mkOption {
807 type = lib.types.str;
808 description = "Credentials endpoint for the S3 storage backend.";
809 default = "";
810 };
811 awsContainerCredentialsRelativeURI = lib.mkOption {
812 type = lib.types.nullOr lib.types.str;
813 description = "Container relative url (used by fargate).";
814 default = null;
815 };
816 endpoint = lib.mkOption {
817 type = lib.types.nullOr lib.types.str;
818 description = "Endpoint for the S3 storage backend.";
819 default = null;
820 };
821 };
822
823 azureblob = {
824 accountName = lib.mkOption {
825 type = lib.types.nullOr lib.types.str;
826 description = "Account name for the Azure Blob storage backend.";
827 default = null;
828 };
829 accountKey = lib.mkOption {
830 type = lib.types.nullOr lib.types.str;
831 description = "Account key for the Azure Blob storage backend. Warning: this is stored in plain text in the config file.";
832 default = null;
833 };
834 containerName = lib.mkOption {
835 type = lib.types.nullOr lib.types.str;
836 description = "Container name for the Azure Blob storage backend.";
837 default = null;
838 };
839 };
840
841 external = {
842 url = lib.mkOption {
843 type = lib.types.nullOr lib.types.str;
844 description = "URL of the backend storage layer.";
845 example = "https://athens.example.com";
846 default = null;
847 };
848 };
849 };
850
851 index = {
852 mysql = {
853 protocol = lib.mkOption {
854 type = lib.types.str;
855 description = "Protocol for the MySQL database.";
856 default = "tcp";
857 };
858 host = lib.mkOption {
859 type = lib.types.str;
860 description = "Host for the MySQL database.";
861 default = "localhost";
862 };
863 port = lib.mkOption {
864 type = lib.types.int;
865 description = "Port for the MySQL database.";
866 default = 3306;
867 };
868 user = lib.mkOption {
869 type = lib.types.str;
870 description = "User for the MySQL database.";
871 default = "root";
872 };
873 password = lib.mkOption {
874 type = lib.types.nullOr lib.types.str;
875 description = "Password for the MySQL database. Warning: this is stored in plain text in the config file.";
876 default = null;
877 };
878 database = lib.mkOption {
879 type = lib.types.str;
880 description = "Database name for the MySQL database.";
881 default = "athens";
882 };
883 params = {
884 parseTime = lib.mkOption {
885 type = lib.types.nullOr lib.types.str;
886 description = "Parse time for the MySQL database.";
887 default = "true";
888 };
889 timeout = lib.mkOption {
890 type = lib.types.nullOr lib.types.str;
891 description = "Timeout for the MySQL database.";
892 default = "30s";
893 };
894 };
895 };
896
897 postgres = {
898 host = lib.mkOption {
899 type = lib.types.str;
900 description = "Host for the Postgres database.";
901 default = "localhost";
902 };
903 port = lib.mkOption {
904 type = lib.types.int;
905 description = "Port for the Postgres database.";
906 default = 5432;
907 };
908 user = lib.mkOption {
909 type = lib.types.str;
910 description = "User for the Postgres database.";
911 default = "postgres";
912 };
913 password = lib.mkOption {
914 type = lib.types.nullOr lib.types.str;
915 description = "Password for the Postgres database. Warning: this is stored in plain text in the config file.";
916 default = null;
917 };
918 database = lib.mkOption {
919 type = lib.types.str;
920 description = "Database name for the Postgres database.";
921 default = "athens";
922 };
923 params = {
924 connect_timeout = lib.mkOption {
925 type = lib.types.nullOr lib.types.str;
926 description = "Connect timeout for the Postgres database.";
927 default = "30s";
928 };
929 sslmode = lib.mkOption {
930 type = lib.types.nullOr lib.types.str;
931 description = "SSL mode for the Postgres database.";
932 default = "disable";
933 };
934 };
935 };
936 };
937
938 extraConfig = lib.mkOption {
939 type = lib.types.attrs;
940 description = ''
941 Extra configuration options for the athens config file.
942 '';
943 default = { };
944 };
945 };
946
947 config = lib.mkIf cfg.enable {
948 systemd.services.athens = {
949 description = "Athens Go module proxy";
950 documentation = [ "https://docs.gomods.io" ];
951
952 wantedBy = [ "multi-user.target" ];
953 after = [ "network-online.target" ];
954 wants = [ "network-online.target" ];
955
956 serviceConfig = {
957 Restart = "on-abnormal";
958 Nice = 5;
959 ExecStart = ''${cfg.package}/bin/athens -config_file=${configFile}'';
960
961 KillMode = "mixed";
962 KillSignal = "SIGINT";
963 TimeoutStopSec = cfg.shutdownTimeout;
964
965 LimitNOFILE = 1048576;
966 LimitNPROC = 512;
967
968 DynamicUser = true;
969 PrivateTmp = true;
970 PrivateDevices = true;
971 ProtectHome = "read-only";
972 ProtectSystem = "full";
973
974 ReadWritePaths = lib.mkIf (
975 cfg.storage.disk.rootPath != null && (!lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath)
976 ) [ cfg.storage.disk.rootPath ];
977 StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath) [
978 (lib.removePrefix "/var/lib/" cfg.storage.disk.rootPath)
979 ];
980
981 CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
982 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
983 NoNewPrivileges = true;
984 };
985 };
986
987 networking.firewall = {
988 allowedTCPPorts =
989 lib.optionals (cfg.unixSocket == null) [ cfg.port ]
990 ++ lib.optionals cfg.enablePprof [ cfg.pprofPort ];
991 };
992 };
993
994}