diff options
Diffstat (limited to 'nixops')
-rw-r--r-- | nixops/eldiron.nix | 2 | ||||
-rw-r--r-- | nixops/modules/databases/immae.schema | 16 | ||||
-rw-r--r-- | nixops/modules/task/default.nix | 131 | ||||
-rw-r--r-- | nixops/modules/task/www/index.php | 135 |
4 files changed, 282 insertions, 2 deletions
diff --git a/nixops/eldiron.nix b/nixops/eldiron.nix index 5f0b5d5..21d3390 100644 --- a/nixops/eldiron.nix +++ b/nixops/eldiron.nix | |||
@@ -31,6 +31,7 @@ | |||
31 | ./modules/mail | 31 | ./modules/mail |
32 | ./modules/ftp | 32 | ./modules/ftp |
33 | ./modules/pub | 33 | ./modules/pub |
34 | ./modules/task | ||
34 | ]; | 35 | ]; |
35 | services.myGitolite.enable = true; | 36 | services.myGitolite.enable = true; |
36 | services.myDatabases.enable = true; | 37 | services.myDatabases.enable = true; |
@@ -39,6 +40,7 @@ | |||
39 | services.myWebsites.tools.enable = true; | 40 | services.myWebsites.tools.enable = true; |
40 | services.pure-ftpd.enable = true; | 41 | services.pure-ftpd.enable = true; |
41 | services.pub.enable = true; | 42 | services.pub.enable = true; |
43 | services.myTasks.enable = true; | ||
42 | 44 | ||
43 | services.journald.extraConfig = '' | 45 | services.journald.extraConfig = '' |
44 | MaxLevelStore="warning" | 46 | MaxLevelStore="warning" |
diff --git a/nixops/modules/databases/immae.schema b/nixops/modules/databases/immae.schema index f0e12bc..f5ee5d5 100644 --- a/nixops/modules/databases/immae.schema +++ b/nixops/modules/databases/immae.schema | |||
@@ -149,7 +149,19 @@ objectclass ( ImmaeobjectClass:8 NAME 'immaePuppetClass' | |||
149 | MUST ( immaePuppetJson ) | 149 | MUST ( immaePuppetJson ) |
150 | ) | 150 | ) |
151 | 151 | ||
152 | attributetype (ImmaeattributeType:19 NAME 'immaeTaskId' | ||
153 | DESC 'Taskwarrior server Org:Name:Key' | ||
154 | EQUALITY caseIgnoreMatch | ||
155 | SUBSTR caseIgnoreSubstringsMatch | ||
156 | SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) | ||
157 | |||
158 | objectclass ( ImmaeobjectClass:9 NAME 'immaeTaskClass' | ||
159 | DESC 'Expansion of the existing object classes for Task' | ||
160 | SUP top AUXILIARY | ||
161 | MUST ( immaeTaskId ) | ||
162 | ) | ||
163 | |||
152 | # Last: | 164 | # Last: |
153 | # attributetype (ImmaeattributeType:18 NAME 'immaeAccessReadSubtree' | 165 | # attributetype (ImmaeattributeType:19 NAME 'immaeTaskId' |
154 | # objectclass ( ImmaeobjectClass:8 NAME 'immaePuppetClass' | 166 | # objectclass ( ImmaeobjectClass:9 NAME 'immaeTaskClass' |
155 | 167 | ||
diff --git a/nixops/modules/task/default.nix b/nixops/modules/task/default.nix new file mode 100644 index 0000000..3dc3299 --- /dev/null +++ b/nixops/modules/task/default.nix | |||
@@ -0,0 +1,131 @@ | |||
1 | { lib, pkgs, config, myconfig, mylibs, ... }: | ||
2 | let | ||
3 | cfg = config.services.myTasks; | ||
4 | vardir = config.services.taskserver.dataDir; | ||
5 | fqdn = "task.immae.eu"; | ||
6 | user = config.services.taskserver.user; | ||
7 | env = myconfig.env.tools.task; | ||
8 | group = config.services.taskserver.group; | ||
9 | in { | ||
10 | options.services.myTasks = { | ||
11 | enable = lib.mkEnableOption "my tasks service"; | ||
12 | }; | ||
13 | |||
14 | config = lib.mkIf cfg.enable { | ||
15 | security.acme.certs."eldiron".extraDomains.${fqdn} = null; | ||
16 | services.myWebsites.tools.modules = [ "proxy_fcgi" ]; | ||
17 | services.myWebsites.tools.vhostConfs.task = { | ||
18 | certName = "eldiron"; | ||
19 | hosts = [ "task.immae.eu" ]; | ||
20 | root = "/run/current-system/webapps/_task"; | ||
21 | extraConfig = [ '' | ||
22 | <Directory /run/current-system/webapps/_task> | ||
23 | DirectoryIndex index.php | ||
24 | Use LDAPConnect | ||
25 | Require ldap-group cn=users,cn=taskwarrior,ou=services,dc=immae,dc=eu | ||
26 | <FilesMatch "\.php$"> | ||
27 | SetHandler "proxy:unix:/var/run/phpfpm/task.sock|fcgi://localhost" | ||
28 | </FilesMatch> | ||
29 | SetEnv TASKD_HOST "${fqdn}:${toString config.services.taskserver.listenPort}" | ||
30 | SetEnv TASKD_VARDIR "${vardir}" | ||
31 | SetEnv TASKD_LDAP_HOST "ldaps://${env.ldap.host}" | ||
32 | SetEnv TASKD_LDAP_DN "${env.ldap.dn}" | ||
33 | SetEnv TASKD_LDAP_PASSWORD "${env.ldap.password}" | ||
34 | SetEnv TASKD_LDAP_BASE "${env.ldap.base}" | ||
35 | SetEnv TASKD_LDAP_FILTER "${env.ldap.search}" | ||
36 | </Directory> | ||
37 | '' ]; | ||
38 | }; | ||
39 | services.myPhpfpm.poolConfigs = { | ||
40 | tasks = '' | ||
41 | listen = /var/run/phpfpm/task.sock | ||
42 | user = ${user} | ||
43 | group = ${group} | ||
44 | listen.owner = wwwrun | ||
45 | listen.group = wwwrun | ||
46 | pm = dynamic | ||
47 | pm.max_children = 60 | ||
48 | pm.start_servers = 2 | ||
49 | pm.min_spare_servers = 1 | ||
50 | pm.max_spare_servers = 10 | ||
51 | |||
52 | ; Needed to avoid clashes in browser cookies (same domain) | ||
53 | env[PATH] = "/etc/profiles/per-user/${user}/bin" | ||
54 | php_value[session.name] = TaskPHPSESSID | ||
55 | php_admin_value[open_basedir] = "${./www}:/tmp:${vardir}:/etc/profiles/per-user/${user}/bin/" | ||
56 | ''; | ||
57 | }; | ||
58 | |||
59 | system.extraSystemBuilderCmds = '' | ||
60 | ln -s ${./www} $out/webapps/_task | ||
61 | ''; | ||
62 | |||
63 | security.acme.certs."task" = config.services.myCertificates.certConfig // { | ||
64 | inherit user group; | ||
65 | plugins = [ "fullchain.pem" "key.pem" "cert.pem" "account_key.json" ]; | ||
66 | domain = fqdn; | ||
67 | postRun = '' | ||
68 | systemctl restart taskserver.service | ||
69 | ''; | ||
70 | }; | ||
71 | |||
72 | users.users.${user}.packages = [ | ||
73 | (pkgs.runCommand "taskserver-user-certs" {} '' | ||
74 | mkdir -p $out/bin | ||
75 | cat > $out/bin/taskserver-user-certs <<"EOF" | ||
76 | #!/usr/bin/env bash | ||
77 | |||
78 | user=$1 | ||
79 | |||
80 | silent_certtool() { | ||
81 | if ! output="$("${pkgs.gnutls.bin}/bin/certtool" "$@" 2>&1)"; then | ||
82 | echo "GNUTLS certtool invocation failed with output:" >&2 | ||
83 | echo "$output" >&2 | ||
84 | fi | ||
85 | } | ||
86 | |||
87 | silent_certtool -p \ | ||
88 | --bits 4096 \ | ||
89 | --outfile "${vardir}/userkeys/$user.key.pem" | ||
90 | ${pkgs.gnused}/bin/sed -i -n -e '/^-----BEGIN RSA PRIVATE KEY-----$/,$p' "${vardir}/userkeys/$user.key.pem" | ||
91 | |||
92 | silent_certtool -c \ | ||
93 | --template "${pkgs.writeText "taskserver-ca.template" '' | ||
94 | tls_www_client | ||
95 | encryption_key | ||
96 | signing_key | ||
97 | expiration_days = 3650 | ||
98 | ''}" \ | ||
99 | --load-ca-certificate "${vardir}/keys/ca.cert" \ | ||
100 | --load-ca-privkey "${vardir}/keys/ca.key" \ | ||
101 | --load-privkey "${vardir}/userkeys/$user.key.pem" \ | ||
102 | --outfile "${vardir}/userkeys/$user.cert.pem" | ||
103 | EOF | ||
104 | chmod a+x $out/bin/taskserver-user-certs | ||
105 | patchShebangs $out/bin/taskserver-user-certs | ||
106 | '') | ||
107 | ]; | ||
108 | |||
109 | systemd.services.taskserver-ca.postStart = '' | ||
110 | chown :${group} "${vardir}/keys/ca.key" | ||
111 | chmod g+r "${vardir}/keys/ca.key" | ||
112 | ''; | ||
113 | |||
114 | system.activationScripts.taskserver = { | ||
115 | deps = [ "users" ]; | ||
116 | text = '' | ||
117 | install -m 0750 -o ${user} -g ${group} -d ${vardir} | ||
118 | install -m 0750 -o ${user} -g ${group} -d ${vardir}/userkeys | ||
119 | install -m 0750 -o ${user} -g ${group} -d ${vardir}/keys | ||
120 | ''; | ||
121 | }; | ||
122 | |||
123 | services.taskserver = { | ||
124 | enable = true; | ||
125 | allowedClientIDs = [ "^task [2-9]" "^Mirakel [1-9]" ]; | ||
126 | inherit fqdn; | ||
127 | listenHost = "::"; | ||
128 | requestLimit = 104857600; | ||
129 | }; | ||
130 | }; | ||
131 | } | ||
diff --git a/nixops/modules/task/www/index.php b/nixops/modules/task/www/index.php new file mode 100644 index 0000000..829cdd0 --- /dev/null +++ b/nixops/modules/task/www/index.php | |||
@@ -0,0 +1,135 @@ | |||
1 | <?php | ||
2 | if (!isset($_SERVER["REMOTE_USER"])) { | ||
3 | die("please login"); | ||
4 | } | ||
5 | $ldap_user = $_SERVER["REMOTE_USER"]; | ||
6 | $ldap_host = getenv("TASKD_LDAP_HOST"); | ||
7 | $ldap_dn = getenv('TASKD_LDAP_DN'); | ||
8 | $ldap_password = getenv('TASKD_LDAP_PASSWORD'); | ||
9 | $ldap_base = getenv('TASKD_LDAP_BASE'); | ||
10 | $ldap_filter = getenv('TASKD_LDAP_FILTER'); | ||
11 | $host = getenv('TASKD_HOST'); | ||
12 | $vardir = getenv('TASKD_VARDIR'); | ||
13 | |||
14 | $connect = ldap_connect($ldap_host); | ||
15 | ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3); | ||
16 | if (!$connect || !ldap_bind($connect, $ldap_dn, $ldap_password)) { | ||
17 | die("impossible to connect to LDAP"); | ||
18 | } | ||
19 | |||
20 | $search_query = str_replace('%login%', ldap_escape($ldap_user), $ldap_filter); | ||
21 | |||
22 | $search = ldap_search($connect, $ldap_base, $search_query); | ||
23 | $info = ldap_get_entries($connect, $search); | ||
24 | |||
25 | if (ldap_count_entries($connect, $search) != 1) { | ||
26 | die("Impossible to find user in LDAP"); | ||
27 | } | ||
28 | |||
29 | $entries = []; | ||
30 | foreach($info[0]["immaetaskid"] as $key => $value) { | ||
31 | if ($key !== "count") { | ||
32 | $entries[] = explode(":", $value); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | if (isset($_GET["file"])) { | ||
37 | $basecert = $vardir . "/userkeys/" . $ldap_user; | ||
38 | if (!file_exists($basecert . ".cert.pem")) { | ||
39 | exec("taskserver-user-certs $ldap_user"); | ||
40 | } | ||
41 | $certificate = file_get_contents($basecert . ".cert.pem"); | ||
42 | $cert_key = file_get_contents($basecert . ".key.pem"); | ||
43 | $server_cert = file_get_contents($vardir . "/keys/server.cert"); | ||
44 | |||
45 | $file = $_GET["file"]; | ||
46 | switch($file) { | ||
47 | case "ca.cert.pem": | ||
48 | $content = $server_cert; | ||
49 | $name = "ca.cert.pem"; | ||
50 | $type = "application/x-x509-ca-cert"; | ||
51 | break; | ||
52 | case "cert.pem": | ||
53 | $content = $certificate; | ||
54 | $name = $ldap_user . ".cert.pem"; | ||
55 | $type = "application/x-x509-ca-cert"; | ||
56 | break; | ||
57 | case "key.pem": | ||
58 | $content = $cert_key; | ||
59 | $name = $ldap_user . ".key.pem"; | ||
60 | $type = "application/x-x509-ca-cert"; | ||
61 | break; | ||
62 | case "mirakel"; | ||
63 | foreach ($entries as $entry) { | ||
64 | list($org, $user, $key) = $entry; | ||
65 | if ($key == $_GET["key"]) { break; } | ||
66 | } | ||
67 | $name = $user . ".mirakel"; | ||
68 | $type = "text/plain"; | ||
69 | $content = "username: $user | ||
70 | org: $org | ||
71 | user key: $key | ||
72 | server: $host | ||
73 | client.cert: | ||
74 | $certificate | ||
75 | Client.key: | ||
76 | $cert_key | ||
77 | ca.cert: | ||
78 | $server_cert | ||
79 | "; | ||
80 | break; | ||
81 | default: | ||
82 | die("invalid file name"); | ||
83 | break; | ||
84 | } | ||
85 | |||
86 | header("Content-Type: $type"); | ||
87 | header('Content-Disposition: attachment; filename="' . $name . '"'); | ||
88 | header('Content-Transfer-Encoding: binary'); | ||
89 | header('Accept-Ranges: bytes'); | ||
90 | header('Cache-Control: private'); | ||
91 | header('Pragma: private'); | ||
92 | echo $content; | ||
93 | exit; | ||
94 | } | ||
95 | ?> | ||
96 | <html> | ||
97 | <header> | ||
98 | <title>Taskwarrior configuration</title> | ||
99 | </header> | ||
100 | <body> | ||
101 | <ul> | ||
102 | <li><a href="?file=ca.cert.pem">ca.cert.pem</a></li> | ||
103 | <li><a href="?file=cert.pem"><?php echo $ldap_user; ?>.cert.pem</a></li> | ||
104 | <li><a href="?file=key.pem"><?php echo $ldap_user; ?>.key.pem</a></li> | ||
105 | </ul> | ||
106 | For command line interface, download the files, put them near your Taskwarrior | ||
107 | configuration files, and add that to your Taskwarrior configuration: | ||
108 | <pre> | ||
109 | taskd.certificate=/path/to/<?php echo $ldap_user; ?>.cert.pem | ||
110 | taskd.key=/path/to/<?php echo $ldap_user; ?>.key.pem | ||
111 | taskd.server=<?php echo $host ."\n"; ?> | ||
112 | <?php if (count($entries) > 1) { | ||
113 | echo "# Chose one of them\n"; | ||
114 | foreach($entries as $entry) { | ||
115 | list($org, $user, $key) = $entry; | ||
116 | echo "# taskd.credentials=$org/$user/$key\n"; | ||
117 | } | ||
118 | } else { ?> | ||
119 | taskd.credentials=<?php echo $entries[0][0]; ?>/<?php echo $entries[0][1]; ?>/<?php echo $entries[0][2]; ?> | ||
120 | <?php } ?> | ||
121 | taskd.ca=/path/to/ca.cert.pem | ||
122 | </pre> | ||
123 | For Mirakel, download and import the file: | ||
124 | <ul> | ||
125 | <?php | ||
126 | foreach ($entries as $entry) { | ||
127 | list($org, $user, $key) = $entry; | ||
128 | echo '<li><a href="?file=mirakel&key='.$key.'">' . $user . '.mirakel</a></li>'; | ||
129 | } | ||
130 | ?> | ||
131 | </ul> | ||
132 | For Android Taskwarrior app, see instructions <a href="https://bitbucket.org/kvorobyev/taskwarriorandroid/wiki/Configuration">here</a>. | ||
133 | </body> | ||
134 | </html> | ||
135 | |||