summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accounts/gkleen@sif/zshrc68
-rw-r--r--hosts/surtr/default.nix2
-rw-r--r--hosts/surtr/email/default.nix70
-rw-r--r--modules/postfwd.nix19
-rw-r--r--overlays/spm/frontend/src/app/spm/spm.component.html5
-rw-r--r--overlays/spm/frontend/src/app/spm/spm.component.sass4
-rw-r--r--overlays/spm/wordlist.txt1
7 files changed, 131 insertions, 38 deletions
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc
index 1b7cc06c..bda73d76 100644
--- a/accounts/gkleen@sif/zshrc
+++ b/accounts/gkleen@sif/zshrc
@@ -45,7 +45,7 @@ genmail() {
45s() { 45s() {
46 dir=$(pwd) 46 dir=$(pwd)
47 [[ ${#@} -ge 1 ]] && dir=$1 47 [[ ${#@} -ge 1 ]] && dir=$1
48 48
49 shellFile=$(findNix ${@}) 49 shellFile=$(findNix ${@})
50 [[ ${#@} -ge 1 ]] && shift 50 [[ ${#@} -ge 1 ]] && shift
51 51
@@ -137,7 +137,7 @@ dir() {
137 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; 137 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
138 esac 138 esac
139 done 139 done
140 140
141 shift $((OPTIND - 1)) 141 shift $((OPTIND - 1))
142 142
143 if [[ -z ${dir} && ${#@} -ge 1 ]]; then 143 if [[ -z ${dir} && ${#@} -ge 1 ]]; then
@@ -145,7 +145,7 @@ dir() {
145 shift 145 shift
146 fi 146 fi
147 147
148 [[ -n ${dir} ]] || return 2; 148 [[ -n ${dir} ]] || return 2;
149 149
150 if [[ ! -e ${dir} ]]; then 150 if [[ ! -e ${dir} ]]; then
151 if [[ -z "${gitWorktree}" ]]; then 151 if [[ -z "${gitWorktree}" ]]; then
@@ -156,7 +156,7 @@ dir() {
156 else 156 else
157 gitWorktree="" 157 gitWorktree=""
158 fi 158 fi
159 159
160 ( 160 (
161 cd ${dir} 161 cd ${dir}
162 export dir; 162 export dir;
@@ -164,7 +164,7 @@ dir() {
164 ${findNix} && { nixShell=$(findNix) || return $? } 164 ${findNix} && { nixShell=$(findNix) || return $? }
165 165
166 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . 166 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} .
167 167
168 if [[ -n ${templateArchive} ]]; then 168 if [[ -n ${templateArchive} ]]; then
169 ( 169 (
170 archiveFile="" 170 archiveFile=""
@@ -181,16 +181,36 @@ dir() {
181 templateArchive=${archiveFile} 181 templateArchive=${archiveFile}
182 fi 182 fi
183 183
184 case $(file --brief --mime-type --dereference ${templateArchive}) in 184 unpack=true
185 application/zip) unzip ${templateArchive} ;; 185 while ${unpack}; do
186 application/vnd.debian.binary-package) 186 case $(file --brief --mime-type --dereference ${templateArchive}) in
187 nix shell nixos#binutils --command ar x ${templateArchive} 187 application/zip)
188 mkdir control data 188 unzip ${templateArchive}
189 tar -C control -xvaf control.* 189 unpack=false
190 tar -C data -xvaf data.* 190 ;;
191 ;; 191 application/vnd.debian.binary-package)
192 *) tar -xvaf ${templateArchive} ;; 192 nix shell nixos#binutils --command ar x ${templateArchive}
193 esac 193 mkdir control data
194 tar -C control -xvaf control.*
195 tar -C data -xvaf data.*
196 unpack=false
197 ;;
198 application/x-rpm)
199 cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio")
200 nix shell nixos#busybox --command rpm2cpio ${templateArchive} > ${cpioArchive}
201 templateArchive=${cpioArchive}
202 unpack=true
203 ;;
204 application/x-cpio)
205 cpio --extract --make-directories --no-absolute-filenames -F ${templateArchive}
206 unpack=false
207 ;;
208 *)
209 tar -xvaf ${templateArchive}
210 unpack=false
211 ;;
212 esac
213 done
194 ) 214 )
195 fi 215 fi
196 216
@@ -202,7 +222,7 @@ dir() {
202 222
203 # typeset -a messages 223 # typeset -a messages
204 # messages=(${(z)$(notmuch search --output=messages ${notmuchMsg})}) 224 # messages=(${(z)$(notmuch search --output=messages ${notmuchMsg})})
205 225
206 # for message (${messages}); do 226 # for message (${messages}); do
207 # typeset -A notmuchAtts 227 # typeset -A notmuchAtts
208 # notmuchAtts=() 228 # notmuchAtts=()
@@ -253,7 +273,7 @@ dir() {
253 quickserve --root . --upload . --show-hidden --tar gz 273 quickserve --root . --upload . --show-hidden --tar gz
254 fi 274 fi
255 275
256 276
257 if [[ ${#@} -eq 0 ]] || ${forceShell}; then 277 if [[ ${#@} -eq 0 ]] || ${forceShell}; then
258 if [[ ${#@} -gt 0 ]]; then 278 if [[ ${#@} -gt 0 ]]; then
259 if [[ -z ${nixShell} ]]; then 279 if [[ -z ${nixShell} ]]; then
@@ -264,11 +284,11 @@ dir() {
264 fi 284 fi
265 285
266 cd $(pwd) # Needed for mounting to work 286 cd $(pwd) # Needed for mounting to work
267 287
268 isSingleDir() { 288 isSingleDir() {
269 typeset -a contents 289 typeset -a contents
270 contents=(*(N) .*(N)) 290 contents=(*(N) .*(N))
271 291
272 if [[ ${#contents} -eq 1 && -d ${contents[1]} ]]; then 292 if [[ ${#contents} -eq 1 && -d ${contents[1]} ]]; then
273 print ${contents[1]} 293 print ${contents[1]}
274 return 0 294 return 0
@@ -278,7 +298,7 @@ dir() {
278 } 298 }
279 while d=$(isSingleDir); do cd ${d}; done 299 while d=$(isSingleDir); do cd ${d}; done
280 300
281 301
282 if [[ -z ${nixShell} ]]; then 302 if [[ -z ${nixShell} ]]; then
283 exec -- zsh 303 exec -- zsh
284 else 304 else
@@ -302,7 +322,7 @@ tmpdir() {
302 printf "Unmounting %s\n" ${1} >&2 322 printf "Unmounting %s\n" ${1} >&2
303 fusermount -u ${1} || umount ${1} || sudo umount ${1} 323 fusermount -u ${1} || umount ${1} || sudo umount ${1}
304 } 324 }
305 325
306 if mountpoint -q -- ${dir}; then 326 if mountpoint -q -- ${dir}; then
307 unmount ${dir} || return $? 327 unmount ${dir} || return $?
308 else 328 else
@@ -311,7 +331,7 @@ tmpdir() {
311 unmount ${subDir} || return $? 331 unmount ${subDir} || return $?
312 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) 332 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr)
313 fi 333 fi
314 334
315 rm -rfv --one-file-system -- ${dir} 335 rm -rfv --one-file-system -- ${dir}
316 } 336 }
317 337
@@ -323,7 +343,7 @@ tmpdir() {
323 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; 343 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
324 esac 344 esac
325 done 345 done
326 346
327 ( 347 (
328 trap cleanup EXIT 348 trap cleanup EXIT
329 349
@@ -362,7 +382,7 @@ nix-ghci() {
362 pkgExpr="${1}" 382 pkgExpr="${1}"
363 shift 383 shift
364 fi 384 fi
365 385
366 nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}" 386 nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}"
367} 387}
368 388
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix
index 2be25560..e031c9b3 100644
--- a/hosts/surtr/default.nix
+++ b/hosts/surtr/default.nix
@@ -57,7 +57,7 @@
57 { address = "202.61.241.61"; prefixLength = 22; } 57 { address = "202.61.241.61"; prefixLength = 22; }
58 ]; 58 ];
59 ipv6.addresses = [ 59 ipv6.addresses = [
60 { address = "2a03:4000:52:ada:98e7:16ff:feba:7a2e"; prefixLength = 128; } 60 # { address = "2a03:4000:52:ada:98e7:16ff:feba:7a2e"; prefixLength = 128; }
61 { address = "2a03:4000:52:ada::"; prefixLength = 96; } 61 { address = "2a03:4000:52:ada::"; prefixLength = 96; }
62 ]; 62 ];
63 }; 63 };
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix
index 9cfba1f1..2fe5b7f0 100644
--- a/hosts/surtr/email/default.nix
+++ b/hosts/surtr/email/default.nix
@@ -59,6 +59,7 @@ in {
59 59
60 services.postfix = { 60 services.postfix = {
61 enable = true; 61 enable = true;
62 enableSmtp = false;
62 hostname = "surtr.yggdrasil.li"; 63 hostname = "surtr.yggdrasil.li";
63 recipientDelimiter = ""; 64 recipientDelimiter = "";
64 setSendmail = true; 65 setSendmail = true;
@@ -66,20 +67,22 @@ in {
66 destination = []; 67 destination = [];
67 sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; 68 sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem";
68 sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; 69 sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem";
69 networks = ["127.0.0.0/8" "[::ffff:127.0.0.0]/104" "[::1]/128" "10.141.0.0/16"]; 70 networks = [];
70 config = let 71 config = let
71 relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}"; 72 relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}";
72 in { 73 in {
74 smtpd_tls_security_level = "may";
75
73 #the dh params 76 #the dh params
74 smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; 77 smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path;
75 smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; 78 smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path;
76 #enable ECDH 79 #enable ECDH
77 smtpd_tls_eecdh_grade = "strong"; 80 smtpd_tls_eecdh_grade = "strong";
78 #enabled SSL protocols, don't allow SSLv2 and SSLv3 81 #enabled SSL protocols, don't allow SSLv2 and SSLv3
79 smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; 82 smtpd_tls_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1"];
80 smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2"]; 83 smtpd_tls_mandatory_protocols = ["!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1"];
81 #allowed ciphers for smtpd_tls_security_level=encrypt 84 #allowed ciphers for smtpd_tls_security_level=encrypt
82 smtpd_tls_mandatory_ciphers = "high"; 85 smtpd_tls_mandatory_ciphers = "medium";
83 #allowed ciphers for smtpd_tls_security_level=may 86 #allowed ciphers for smtpd_tls_security_level=may
84 #smtpd_tls_ciphers = high 87 #smtpd_tls_ciphers = high
85 #enforce the server cipher preference 88 #enforce the server cipher preference
@@ -92,6 +95,7 @@ in {
92 smtpd_tls_loglevel = "1"; 95 smtpd_tls_loglevel = "1";
93 #enable TLS logging to see the ciphers for outbound connections 96 #enable TLS logging to see the ciphers for outbound connections
94 smtp_tls_loglevel = "1"; 97 smtp_tls_loglevel = "1";
98 tls_medium_cipherlist = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
95 99
96 smtpd_tls_received_header = true; 100 smtpd_tls_received_header = true;
97 101
@@ -101,6 +105,8 @@ in {
101 smtp_tls_security_level = "dane"; 105 smtp_tls_security_level = "dane";
102 smtp_dns_support_level = "dnssec"; 106 smtp_dns_support_level = "dnssec";
103 107
108 smtp_tls_connection_reuse = true;
109
104 tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" '' 110 tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" ''
105 bouncy.email /run/credentials/postfix.service/bouncy.email.full.pem 111 bouncy.email /run/credentials/postfix.service/bouncy.email.full.pem
106 mailin.bouncy.email /run/credentials/postfix.service/mailin.bouncy.email.full.pem 112 mailin.bouncy.email /run/credentials/postfix.service/mailin.bouncy.email.full.pem
@@ -130,7 +136,6 @@ in {
130 dbname = email 136 dbname = email
131 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' 137 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s'
132 ''}" 138 ''}"
133 "permit_mynetworks"
134 "check_ccert_access ${relay_ccert}" 139 "check_ccert_access ${relay_ccert}"
135 "reject_non_fqdn_helo_hostname" 140 "reject_non_fqdn_helo_hostname"
136 "reject_invalid_helo_hostname" 141 "reject_invalid_helo_hostname"
@@ -149,14 +154,15 @@ in {
149 address_verify_poll_delay = "1s"; 154 address_verify_poll_delay = "1s";
150 155
151 smtpd_relay_restrictions = [ 156 smtpd_relay_restrictions = [
152 "permit_mynetworks"
153 "check_ccert_access ${relay_ccert}" 157 "check_ccert_access ${relay_ccert}"
154 "reject_unauth_destination" 158 "reject_unauth_destination"
155 ]; 159 ];
156 160
157 propagate_unmatched_extensions = ["canonical" "virtual" "alias"]; 161 propagate_unmatched_extensions = ["canonical" "virtual" "alias"];
158 smtpd_authorized_verp_clients = "$authorized_verp_clients"; 162 smtpd_authorized_verp_clients = "";
159 authorized_verp_clients = "$mynetworks"; 163 authorized_verp_clients = "";
164
165 smtpd_client_event_limit_exceptions = "";
160 166
161 milter_default_action = "accept"; 167 milter_default_action = "accept";
162 smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; 168 smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"];
@@ -197,6 +203,12 @@ in {
197 ''}''; 203 ''}'';
198 dvlmtp_destination_recipient_limit = "1"; 204 dvlmtp_destination_recipient_limit = "1";
199 virtual_transport = "dvlmtp:unix:/run/postfix/dovecot-lmtp"; 205 virtual_transport = "dvlmtp:unix:/run/postfix/dovecot-lmtp";
206
207 authorized_submit_users = "inline:{ root= postfwd= }";
208
209 postscreen_access_list = "";
210 postscreen_denylist_action = "drop";
211 postscreen_greet_action = "enforce";
200 }; 212 };
201 masterConfig = { 213 masterConfig = {
202 smtps = { 214 smtps = {
@@ -204,6 +216,14 @@ in {
204 private = false; 216 private = false;
205 command = "smtpd"; 217 command = "smtpd";
206 args = [ 218 args = [
219 "-o" "smtpd_tls_security_level=encrypt"
220 "-o" "{smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}"
221 "-o" "{smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2}"
222 "-o" "smtpd_tls_mandatory_ciphers=high"
223 "-o" "smtpd_tls_dh1024_param_file=${toString config.security.dhparams.params."postfix-smtps-1024".path}"
224 "-o" "smtpd_tls_dh512_param_file=${toString config.security.dhparams.params."postfix-smtps-512".path}"
225 "-o" "{tls_eecdh_auto_curves = X25519 X448}"
226
207 "-o" "smtpd_tls_wrappermode=yes" 227 "-o" "smtpd_tls_wrappermode=yes"
208 "-o" "smtpd_tls_ask_ccert=yes" 228 "-o" "smtpd_tls_ask_ccert=yes"
209 "-o" "smtpd_tls_req_ccert=yes" 229 "-o" "smtpd_tls_req_ccert=yes"
@@ -224,6 +244,27 @@ in {
224 "flags=DORX" 244 "flags=DORX"
225 ]; 245 ];
226 }; 246 };
247 smtp_pass = {
248 name = "smtpd";
249 type = "pass";
250 command = "smtpd";
251 };
252 postscreen = {
253 name = "smtp";
254 type = "inet";
255 private = false;
256 command = "postscreen";
257 maxproc = 1;
258 };
259 smtp = {};
260 relay = {
261 command = "smtp";
262 args = [ "-o" "smtp_fallback_relay=" ];
263 };
264 tlsproxy = {
265 maxproc = 0;
266 };
267 dnsblog = {};
227 }; 268 };
228 }; 269 };
229 270
@@ -596,6 +637,9 @@ in {
596 params = { 637 params = {
597 "postfix-512".bits = 512; 638 "postfix-512".bits = 512;
598 "postfix-1024".bits = 2048; 639 "postfix-1024".bits = 2048;
640
641 "postfix-smtps-512".bits = 512;
642 "postfix-smtps-1024".bits = 2048;
599 }; 643 };
600 }; 644 };
601 645
@@ -800,8 +844,14 @@ in {
800 services.postfwd = { 844 services.postfwd = {
801 enable = true; 845 enable = true;
802 rules = '' 846 rules = ''
803 id=RCPT01; protocol_state=DATA; protocol_state=END-OF-MESSAGE; action=rcpt(ccert_subject/100/3600/450 4.7.1 Exceeding maximum of 100 recipients per hour [$$ratecount]) 847 id=RCPT01; protocol_state=DATA; protocol_state=END-OF-MESSAGE; action=rcpt(ccert_subject/100/3600/set(HIT_RATELIMIT=1,HIT_RATECOUNT=$$ratecount,HIT_RATELIMIT_LIMIT=100,HIT_RATELIMIT_INTERVAL=3600))
804 id=RCPT02; protocol_state=DATA; protocol_state=END-OF-MESSAGE; action=rcpt(ccert_subject/1000/86400/450 4.7.1 Exceeding maximum of 1000 recipients per day [$$ratecount]) 848 id=RCPT02; protocol_state=DATA; protocol_state=END-OF-MESSAGE; action=rcpt(ccert_subject/1000/86400/set(HIT_RATELIMIT=1,HIT_RATECOUNT=$$ratecount,HIT_RATELIMIT_LIMIT=1000,HIT_RATELIMIT_INTERVAL=86400))
849
850 id=JUMP_REJECT_RL; HIT_RATELIMIT=="1"; action=jump(REJECT_RL)
851
852 id=EOF; action=DUNNO
853
854 id=REJECT_RL; action=450 4.7.1 Exceeding maximum of $$HIT_RATELIMIT_LIMIT recipients per $$HIT_RATELIMIT_INTERVAL seconds [$$HIT_RATECOUNT]
805 ''; 855 '';
806 }; 856 };
807 }; 857 };
diff --git a/modules/postfwd.nix b/modules/postfwd.nix
index 4afea0a1..e10c04a7 100644
--- a/modules/postfwd.nix
+++ b/modules/postfwd.nix
@@ -32,6 +32,11 @@ in {
32 "--proto" "unix" 32 "--proto" "unix"
33 "--port" "/run/postfwd3/postfwd3.sock" 33 "--port" "/run/postfwd3/postfwd3.sock"
34 "--save_rates" "/var/lib/postfwd/rates" 34 "--save_rates" "/var/lib/postfwd/rates"
35 "--save_groups" "/var/lib/postfwd/groups"
36 "--summary" "3600"
37 "--cache" "600"
38 "--cache_proto" "unix"
39 "--cache_port" "/run/postfwd3/cache.sock"
35 "--file" (pkgs.writeText "postfwd3-rules" cfg.rules) 40 "--file" (pkgs.writeText "postfwd3-rules" cfg.rules)
36 ]}"; 41 ]}";
37 PIDFile = "/run/postfwd3/postfwd3.pid"; 42 PIDFile = "/run/postfwd3/postfwd3.pid";
@@ -45,7 +50,8 @@ in {
45 50
46 DynamicUser = true; 51 DynamicUser = true;
47 ProtectSystem = "strict"; 52 ProtectSystem = "strict";
48 SystemCallFilter = "@system-service"; 53 ProtectHome = true;
54 SystemCallFilter = ["@system-service" "~@resources @obsolete"];
49 NoNewPrivileges = true; 55 NoNewPrivileges = true;
50 ProtectKernelTunables = true; 56 ProtectKernelTunables = true;
51 ProtectKernelModules = true; 57 ProtectKernelModules = true;
@@ -59,6 +65,17 @@ in {
59 PrivateDevices = true; 65 PrivateDevices = true;
60 PrivateTmp = true; 66 PrivateTmp = true;
61 ProtectHostname = true; 67 ProtectHostname = true;
68 RestrictNamespaces = true;
69 CapabilityBoundingSet = "";
70 RestrictAddressFamilies = ["AF_UNIX"];
71 PrivateNetwork = true;
72 PrivateUsers = true;
73 SystemCallArchitectures = "native";
74 LockPersonality = true;
75 ProtectProc = "invisible";
76 ProcSubset = "pid";
77 DevicePolicy = "closed";
78 IPAddressDeny = "any";
62 }; 79 };
63 }; 80 };
64 }; 81 };
diff --git a/overlays/spm/frontend/src/app/spm/spm.component.html b/overlays/spm/frontend/src/app/spm/spm.component.html
index 5d0e625a..416da91f 100644
--- a/overlays/spm/frontend/src/app/spm/spm.component.html
+++ b/overlays/spm/frontend/src/app/spm/spm.component.html
@@ -1,7 +1,10 @@
1<div id="mail-panel-container" fxLayout="row wrap" style="gap: 16px"> 1<div id="mail-panel-container" fxLayout="row wrap" style="gap: 16px">
2 <ng-template ngFor [ngForOf]="spmMails$ | async | keyvalue: asIsOrder" let-entry> 2 <ng-template ngFor [ngForOf]="spmMails$ | async | keyvalue: asIsOrder" let-entry>
3 <mat-card> 3 <mat-card>
4 <mat-card-title class="mono" *ngIf="entry.value.state !== 'loading'">{{entry.value.local}}</mat-card-title> 4 <mat-card-title class="mono" *ngIf="entry.value.state !== 'loading' && entry.value.state !== 'claimed'">{{entry.value.local}}</mat-card-title>
5 <mat-card-title class="mono" *ngIf="entry.value.state === 'claimed'">
6 <a href="mailto:{{entry.value.local}}@{{entry.value.domain}}">{{entry.value.local}}</a>
7 </mat-card-title>
5 <mat-card-subtitle class="mono" *ngIf="entry.value.state !== 'loading'">@{{entry.value.domain}}</mat-card-subtitle> 8 <mat-card-subtitle class="mono" *ngIf="entry.value.state !== 'loading'">@{{entry.value.domain}}</mat-card-subtitle>
6 <mat-card-content *ngIf="entry.value.state === 'loading'"> 9 <mat-card-content *ngIf="entry.value.state === 'loading'">
7 <mat-spinner style="margin: auto"></mat-spinner> 10 <mat-spinner style="margin: auto"></mat-spinner>
diff --git a/overlays/spm/frontend/src/app/spm/spm.component.sass b/overlays/spm/frontend/src/app/spm/spm.component.sass
index 74ad7d0e..773eb879 100644
--- a/overlays/spm/frontend/src/app/spm/spm.component.sass
+++ b/overlays/spm/frontend/src/app/spm/spm.component.sass
@@ -1,3 +1,7 @@
1a
2 color: inherit
3 text-decoration: inherit
4
1#add-button 5#add-button
2 position: fixed 6 position: fixed
3 bottom: 16px 7 bottom: 16px
diff --git a/overlays/spm/wordlist.txt b/overlays/spm/wordlist.txt
index 028ca87e..0db5ca44 100644
--- a/overlays/spm/wordlist.txt
+++ b/overlays/spm/wordlist.txt
@@ -605,7 +605,6 @@ keg
605kept 605kept
606kick 606kick
607kilt 607kilt
608king
609kite 608kite
610kitty 609kitty
611kiwi 610kiwi