commit
a24431e56f
@ -0,0 +1,162 @@ |
||||
{ config, pkgs, lib, ... }: |
||||
let |
||||
cfg = config.services.schleuder; |
||||
settingsFormat = pkgs.formats.yaml { }; |
||||
postfixMap = entries: lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${value}") entries); |
||||
writePostfixMap = name: entries: pkgs.writeText name (postfixMap entries); |
||||
configScript = pkgs.writeScript "schleuder-cfg" '' |
||||
#!${pkgs.runtimeShell} |
||||
set -exuo pipefail |
||||
umask 0077 |
||||
${pkgs.yq}/bin/yq \ |
||||
--slurpfile overrides <(${pkgs.yq}/bin/yq . <${lib.escapeShellArg cfg.extraSettingsFile}) \ |
||||
< ${settingsFormat.generate "schleuder.yml" cfg.settings} \ |
||||
'. * $overrides[0]' \ |
||||
> /etc/schleuder/schleuder.yml |
||||
chown schleuder: /etc/schleuder/schleuder.yml |
||||
''; |
||||
in |
||||
{ |
||||
options.services.schleuder = { |
||||
enable = lib.mkEnableOption "Schleuder secure remailer"; |
||||
enablePostfix = lib.mkEnableOption "automatic postfix integration" // { default = true; }; |
||||
lists = lib.mkOption { |
||||
description = '' |
||||
List of list addresses that should be handled by Schleuder. |
||||
|
||||
Note that this is only handled by the postfix integration, and |
||||
the setup of the lists, their members and their keys has to be |
||||
performed separately via schleuder's API, using a tool such as |
||||
schleuder-cli. |
||||
''; |
||||
type = lib.types.listOf lib.types.str; |
||||
default = [ ]; |
||||
example = [ "widget-team@example.com" "security@example.com" ]; |
||||
}; |
||||
/* maybe one day.... |
||||
domains = lib.mkOption { |
||||
description = "Domains for which all mail should be handled by Schleuder."; |
||||
type = lib.types.listOf lib.types.str; |
||||
default = []; |
||||
example = ["securelists.example.com"]; |
||||
}; |
||||
*/ |
||||
settings = lib.mkOption { |
||||
description = '' |
||||
Settings for schleuder.yml. |
||||
|
||||
Check the <link xlink:href="https://0xacab.org/schleuder/schleuder/blob/master/etc/schleuder.yml">example configuration</link> for possible values. |
||||
''; |
||||
type = lib.types.submodule { |
||||
freeformType = settingsFormat.type; |
||||
options.keyserver = lib.mkOption { |
||||
type = lib.types.str; |
||||
description = '' |
||||
Key server from which to fetch and update keys. |
||||
|
||||
Note that NixOS uses a different default from upstream, since the upstream default sks-keyservers.net is deprecated. |
||||
''; |
||||
default = "keys.openpgp.org"; |
||||
}; |
||||
}; |
||||
default = { }; |
||||
}; |
||||
extraSettingsFile = lib.mkOption { |
||||
description = "YAML file to merge into the schleuder config at runtime. This can be used for secrets such as API keys."; |
||||
type = lib.types.nullOr lib.types.path; |
||||
default = null; |
||||
}; |
||||
listDefaults = lib.mkOption { |
||||
description = '' |
||||
Default settings for lists (list-defaults.yml). |
||||
|
||||
Check the <link xlink:href="https://0xacab.org/schleuder/schleuder/-/blob/master/etc/list-defaults.yml">example configuration</link> for possible values. |
||||
''; |
||||
type = settingsFormat.type; |
||||
default = { }; |
||||
}; |
||||
}; |
||||
config = lib.mkIf cfg.enable { |
||||
assertions = [ |
||||
{ |
||||
assertion = !(cfg.settings.api ? valid_api_keys); |
||||
message = '' |
||||
services.schleuder.settings.api.valid_api_keys is set. Defining API keys via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store API keys in a non-public location. |
||||
''; |
||||
} |
||||
{ |
||||
assertion = !(lib.any (db: db ? password) (lib.attrValues cfg.settings.database or {})); |
||||
message = '' |
||||
A password is defined for at least one database in services.schleuder.settings.database. Defining passwords via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store database passwords in a non-public location. |
||||
''; |
||||
} |
||||
]; |
||||
users.users.schleuder.isSystemUser = true; |
||||
users.users.schleuder.group = "schleuder"; |
||||
users.groups.schleuder = {}; |
||||
environment.systemPackages = [ |
||||
pkgs.schleuder-cli |
||||
]; |
||||
services.postfix = lib.mkIf cfg.enablePostfix { |
||||
extraMasterConf = '' |
||||
schleuder unix - n n - - pipe |
||||
flags=DRhu user=schleuder argv=/${pkgs.schleuder}/bin/schleuder work ''${recipient} |
||||
''; |
||||
transport = lib.mkIf (cfg.lists != [ ]) (postfixMap (lib.genAttrs cfg.lists (_: "schleuder:"))); |
||||
extraConfig = '' |
||||
schleuder_destination_recipient_limit = 1 |
||||
''; |
||||
# review: does this make sense? |
||||
localRecipients = lib.mkIf (cfg.lists != [ ]) cfg.lists; |
||||
}; |
||||
systemd.services = let commonServiceConfig = { |
||||
# We would have liked to use DynamicUser, but since the default |
||||
# database is SQLite and lives in StateDirectory, and that same |
||||
# database needs to be readable from the postfix service, this |
||||
# isn't trivial to do. |
||||
User = "schleuder"; |
||||
StateDirectory = "schleuder"; |
||||
StateDirectoryMode = "0700"; |
||||
}; in |
||||
{ |
||||
schleuder-init = { |
||||
serviceConfig = commonServiceConfig // { |
||||
ExecStartPre = lib.mkIf (cfg.extraSettingsFile != null) [ |
||||
"+${configScript}" |
||||
]; |
||||
ExecStart = [ "${pkgs.schleuder}/bin/schleuder install" ]; |
||||
Type = "oneshot"; |
||||
}; |
||||
}; |
||||
schleuder-api-daemon = { |
||||
after = [ "local-fs.target" "network.target" "schleuder-init.service" ]; |
||||
wantedBy = [ "multi-user.target" ]; |
||||
requires = [ "schleuder-init.service" ]; |
||||
serviceConfig = commonServiceConfig // { |
||||
ExecStart = [ "${pkgs.schleuder}/bin/schleuder-api-daemon" ]; |
||||
}; |
||||
}; |
||||
schleuder-weekly-key-maintenance = { |
||||
after = [ "local-fs.target" "network.target" ]; |
||||
startAt = "weekly"; |
||||
serviceConfig = commonServiceConfig // { |
||||
ExecStart = [ |
||||
"${pkgs.schleuder}/bin/schleuder refresh_keys" |
||||
"${pkgs.schleuder}/bin/schleuder check_keys" |
||||
]; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
environment.etc."schleuder/schleuder.yml" = lib.mkIf (cfg.extraSettingsFile == null) { |
||||
source = settingsFormat.generate "schleuder.yml" cfg.settings; |
||||
}; |
||||
environment.etc."schleuder/list-defaults.yml".source = settingsFormat.generate "list-defaults.yml" cfg.listDefaults; |
||||
|
||||
services.schleuder = { |
||||
#lists_dir = "/var/lib/schleuder.lists"; |
||||
settings.filters_dir = lib.mkDefault "/var/lib/schleuder/filters"; |
||||
settings.keyword_handlers_dir = lib.mkDefault "/var/lib/schleuder/keyword_handlers"; |
||||
}; |
||||
}; |
||||
} |
@ -0,0 +1,128 @@ |
||||
let |
||||
certs = import ./common/acme/server/snakeoil-certs.nix; |
||||
domain = certs.domain; |
||||
in |
||||
import ./make-test-python.nix { |
||||
name = "schleuder"; |
||||
nodes.machine = { pkgs, ... }: { |
||||
imports = [ ./common/user-account.nix ]; |
||||
services.postfix = { |
||||
enable = true; |
||||
enableSubmission = true; |
||||
tlsTrustedAuthorities = "${certs.ca.cert}"; |
||||
sslCert = "${certs.${domain}.cert}"; |
||||
sslKey = "${certs.${domain}.key}"; |
||||
inherit domain; |
||||
destination = [ domain ]; |
||||
localRecipients = [ "root" "alice" "bob" ]; |
||||
}; |
||||
services.schleuder = { |
||||
enable = true; |
||||
# Don't do it like this in production! The point of this setting |
||||
# is to allow loading secrets from _outside_ the world-readable |
||||
# Nix store. |
||||
extraSettingsFile = pkgs.writeText "schleuder-api-keys.yml" '' |
||||
api: |
||||
valid_api_keys: |
||||
- fnord |
||||
''; |
||||
lists = [ "security@${domain}" ]; |
||||
settings.api = { |
||||
tls_cert_file = "${certs.${domain}.cert}"; |
||||
tls_key_file = "${certs.${domain}.key}"; |
||||
}; |
||||
}; |
||||
|
||||
environment.systemPackages = [ |
||||
pkgs.gnupg |
||||
pkgs.msmtp |
||||
(pkgs.writeScriptBin "do-test" '' |
||||
#!${pkgs.runtimeShell} |
||||
set -exuo pipefail |
||||
|
||||
# Generate a GPG key with no passphrase and export it |
||||
sudo -u alice gpg --passphrase-fd 0 --batch --yes --quick-generate-key 'alice@${domain}' rsa4096 sign,encr < <(echo) |
||||
sudo -u alice gpg --armor --export alice@${domain} > alice.asc |
||||
# Create a new mailing list with alice as the owner, and alice's key |
||||
schleuder-cli list new security@${domain} alice@${domain} alice.asc |
||||
|
||||
# Send an email from a non-member of the list. Use --auto-from so we don't have to specify who it's from twice. |
||||
msmtp --auto-from security@${domain} --host=${domain} --port=25 --tls --tls-starttls <<EOF |
||||
Subject: really big security issue!! |
||||
From: root@${domain} |
||||
|
||||
I found a big security problem! |
||||
EOF |
||||
|
||||
# Wait for delivery |
||||
(set +o pipefail; journalctl -f -n 1000 -u postfix | grep -m 1 'delivered to maildir') |
||||
|
||||
# There should be exactly one email |
||||
mail=(/var/spool/mail/alice/new/*) |
||||
[[ "''${#mail[@]}" = 1 ]] |
||||
|
||||
# Find the fingerprint of the mailing list key |
||||
read list_key_fp address < <(schleuder-cli keys list security@${domain} | grep security@) |
||||
schleuder-cli keys export security@${domain} $list_key_fp > list.asc |
||||
|
||||
# Import the key into alice's keyring, so we can verify it as well as decrypting |
||||
sudo -u alice gpg --import <list.asc |
||||
# And perform the decryption. |
||||
sudo -u alice gpg -d $mail >decrypted |
||||
# And check that the text matches. |
||||
grep "big security problem" decrypted |
||||
'') |
||||
|
||||
# For debugging: |
||||
# pkgs.vim pkgs.openssl pkgs.sqliteinteractive |
||||
]; |
||||
|
||||
security.pki.certificateFiles = [ certs.ca.cert ]; |
||||
|
||||
# Since we don't have internet here, use dnsmasq to provide MX records from /etc/hosts |
||||
services.dnsmasq = { |
||||
enable = true; |
||||
extraConfig = '' |
||||
selfmx |
||||
''; |
||||
}; |
||||
|
||||
networking.extraHosts = '' |
||||
127.0.0.1 ${domain} |
||||
''; |
||||
|
||||
# schleuder-cli's config is not quite optimal in several ways: |
||||
# - A fingerprint _must_ be pinned, it doesn't even have an option |
||||
# to trust the PKI |
||||
# - It compares certificate fingerprints rather than key |
||||
# fingerprints, so renewals break the pin (though that's not |
||||
# relevant for this test) |
||||
# - It compares them as strings, which means we need to match the |
||||
# expected format exactly. This means removing the :s and |
||||
# lowercasing it. |
||||
# Refs: |
||||
# https://0xacab.org/schleuder/schleuder-cli/-/issues/16 |
||||
# https://0xacab.org/schleuder/schleuder-cli/-/blob/f8895b9f47083d8c7b99a2797c93f170f3c6a3c0/lib/schleuder-cli/helper.rb#L230-238 |
||||
systemd.tmpfiles.rules = let cliconfig = pkgs.runCommand "schleuder-cli.yml" |
||||
{ |
||||
nativeBuildInputs = [ pkgs.jq pkgs.openssl ]; |
||||
} '' |
||||
fp=$(openssl x509 -in ${certs.${domain}.cert} -noout -fingerprint -sha256 | cut -d = -f 2 | tr -d : | tr 'A-Z' 'a-z') |
||||
cat > $out <<EOF |
||||
host: localhost |
||||
port: 4443 |
||||
tls_fingerprint: "$fp" |
||||
api_key: fnord |
||||
EOF |
||||
''; in |
||||
[ |
||||
"L+ /root/.schleuder-cli/schleuder-cli.yml - - - - ${cliconfig}" |
||||
]; |
||||
}; |
||||
|
||||
testScript = '' |
||||
machine.wait_for_unit("multi-user.target") |
||||
machine.wait_until_succeeds("nc -z localhost 4443") |
||||
machine.succeed("do-test") |
||||
''; |
||||
} |
@ -0,0 +1,3 @@ |
||||
source 'https://rubygems.org' do |
||||
gem 'schleuder' |
||||
end |
@ -0,0 +1,85 @@ |
||||
GEM |
||||
specs: |
||||
|
||||
GEM |
||||
remote: https://rubygems.org/ |
||||
specs: |
||||
activemodel (6.1.4.4) |
||||
activesupport (= 6.1.4.4) |
||||
activerecord (6.1.4.4) |
||||
activemodel (= 6.1.4.4) |
||||
activesupport (= 6.1.4.4) |
||||
activesupport (6.1.4.4) |
||||
concurrent-ruby (~> 1.0, >= 1.0.2) |
||||
i18n (>= 1.6, < 2) |
||||
minitest (>= 5.1) |
||||
tzinfo (~> 2.0) |
||||
zeitwerk (~> 2.3) |
||||
bcrypt (3.1.16) |
||||
charlock_holmes (0.7.7) |
||||
concurrent-ruby (1.1.9) |
||||
daemons (1.4.1) |
||||
eventmachine (1.2.7) |
||||
gpgme (2.0.20) |
||||
mini_portile2 (~> 2.3) |
||||
i18n (1.8.11) |
||||
concurrent-ruby (~> 1.0) |
||||
mail (2.7.1) |
||||
mini_mime (>= 0.1.1) |
||||
mail-gpg (0.4.4) |
||||
gpgme (~> 2.0, >= 2.0.2) |
||||
mail (~> 2.5, >= 2.5.3) |
||||
mini_mime (1.1.2) |
||||
mini_portile2 (2.7.1) |
||||
minitest (5.15.0) |
||||
multi_json (1.15.0) |
||||
mustermann (1.1.1) |
||||
ruby2_keywords (~> 0.0.1) |
||||
rack (2.2.3) |
||||
rack-protection (2.1.0) |
||||
rack |
||||
rake (13.0.6) |
||||
ruby2_keywords (0.0.5) |
||||
schleuder (4.0.2) |
||||
activerecord (~> 6.1.3) |
||||
bcrypt (~> 3.1.2) |
||||
charlock_holmes (~> 0.7.6) |
||||
gpgme (~> 2.0, >= 2.0.19) |
||||
mail (~> 2.7.1) |
||||
mail-gpg (~> 0.3) |
||||
rake (>= 10.5.0) |
||||
sinatra (~> 2) |
||||
sinatra-contrib (~> 2) |
||||
sqlite3 (~> 1.4.2) |
||||
thin (~> 1) |
||||
thor (~> 0) |
||||
sinatra (2.1.0) |
||||
mustermann (~> 1.0) |
||||
rack (~> 2.2) |
||||
rack-protection (= 2.1.0) |
||||
tilt (~> 2.0) |
||||
sinatra-contrib (2.1.0) |
||||
multi_json |
||||
mustermann (~> 1.0) |
||||
rack-protection (= 2.1.0) |
||||
sinatra (= 2.1.0) |
||||
tilt (~> 2.0) |
||||
sqlite3 (1.4.2) |
||||
thin (1.8.1) |
||||
daemons (~> 1.0, >= 1.0.9) |
||||
eventmachine (~> 1.0, >= 1.0.4) |
||||
rack (>= 1, < 3) |
||||
thor (0.20.3) |
||||
tilt (2.0.10) |
||||
tzinfo (2.0.4) |
||||
concurrent-ruby (~> 1.0) |
||||
zeitwerk (2.5.3) |
||||
|
||||
PLATFORMS |
||||
x86_64-linux |
||||
|
||||
DEPENDENCIES |
||||
schleuder! |
||||
|
||||
BUNDLED WITH |
||||
2.2.24 |
@ -0,0 +1,4 @@ |
||||
source "https://rubygems.org" |
||||
|
||||
gem "schleuder-cli", git: "https://0xacab.org/schleuder/schleuder-cli", tag: "schleuder-cli-0.1.0" |
||||
|
@ -0,0 +1,21 @@ |
||||
GIT |
||||
remote: https://0xacab.org/schleuder/schleuder-cli |
||||
revision: 1de2548695d9a74f47b7868954561b48cbc966f9 |
||||
tag: schleuder-cli-0.1.0 |
||||
specs: |
||||
schleuder-cli (0.1.0) |
||||
thor (~> 0) |
||||
|
||||
GEM |
||||
remote: https://rubygems.org/ |
||||
specs: |
||||
thor (0.20.3) |
||||
|
||||
PLATFORMS |
||||
x86_64-linux |
||||
|
||||
DEPENDENCIES |
||||
schleuder-cli! |
||||
|
||||
BUNDLED WITH |
||||
2.3.6 |
@ -0,0 +1,34 @@ |
||||
{ lib |
||||
, bundlerApp |
||||
, ruby |
||||
, bundlerUpdateScript |
||||
}: |
||||
|
||||
bundlerApp { |
||||
inherit ruby; |
||||
|
||||
pname = "schleuder-cli"; |
||||
|
||||
gemdir = ./.; |
||||
|
||||
installManpages = false; |
||||
|
||||
exes = [ |
||||
"schleuder-cli" |
||||
]; |
||||
|
||||
passthru.updateScript = bundlerUpdateScript "schleuder-cli"; |
||||
|
||||
meta = with lib; { |
||||
description = "A command line tool to create and manage schleuder-lists"; |
||||
longDescription = '' |
||||
Schleuder-cli enables creating, configuring, and deleting lists, |
||||
subscriptions, keys, etc. It uses the Schleuder API, provided by |
||||
schleuder-api-daemon (part of Schleuder). |
||||
''; |
||||
homepage = "https://schleuder.org"; |
||||
changelog = "https://0xacab.org/schleuder/schleuder-cli/-/blob/main/CHANGELOG.md"; |
||||
license = licenses.gpl3Plus; |
||||
maintainers = with maintainers; [ hexa ]; |
||||
}; |
||||
} |
@ -0,0 +1,25 @@ |
||||
{ |
||||
schleuder-cli = { |
||||
dependencies = ["thor"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
fetchSubmodules = false; |
||||
rev = "1de2548695d9a74f47b7868954561b48cbc966f9"; |
||||
sha256 = "0k4i33w9a0bscw4wbs301vxca367g7pa89y6cr24i0014pbmhs9z"; |
||||
type = "git"; |
||||
url = "https://0xacab.org/schleuder/schleuder-cli"; |
||||
}; |
||||
version = "0.1.0"; |
||||
}; |
||||
thor = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "1yhrnp9x8qcy5vc7g438amd5j9sw83ih7c30dr6g6slgw9zj3g29"; |
||||
type = "gem"; |
||||
}; |
||||
version = "0.20.3"; |
||||
}; |
||||
} |
@ -0,0 +1,38 @@ |
||||
{ lib |
||||
, bundlerApp |
||||
, ruby |
||||
, bundlerUpdateScript |
||||
, defaultGemConfig |
||||
, nixosTests |
||||
}: |
||||
|
||||
bundlerApp { |
||||
inherit ruby; |
||||
|
||||
pname = "schleuder"; |
||||
|
||||
gemdir = ./.; |
||||
|
||||
exes = [ |
||||
"schleuder" |
||||
"schleuder-api-daemon" |
||||
]; |
||||
|
||||
passthru.updateScript = bundlerUpdateScript "schleuder"; |
||||
passthru.tests = { |
||||
inherit (nixosTests) schleuder; |
||||
}; |
||||
|
||||
meta = with lib; { |
||||
description = "Schleuder is an encrypting mailing list manager with remailing-capabilities"; |
||||
longDescription = '' |
||||
Schleuder is a group's email-gateway: subscribers can exchange |
||||
encrypted emails among themselves, receive emails from |
||||
non-subscribers and send emails to non-subscribers via the list. |
||||
''; |
||||
homepage = "https://schleuder.org"; |
||||
changelog = "https://0xacab.org/schleuder/schleuder/blob/main/CHANGELOG.md"; |
||||
license = licenses.gpl3Plus; |
||||
maintainers = with maintainers; [ hexa lheckemann ]; |
||||
}; |
||||
} |
@ -0,0 +1,316 @@ |
||||
{ |
||||
activemodel = { |
||||
dependencies = ["activesupport"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0g3qdz8dw6zkgz45jd13lwfdnm7rhgczv1pssw63g9k6qj3bkxjm"; |
||||
type = "gem"; |
||||
}; |
||||
version = "6.1.4.4"; |
||||
}; |
||||
activerecord = { |
||||
dependencies = ["activemodel" "activesupport"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "090d4wl1pq06m9mibpck0m5nm8h45fwhs3fjx27297kjmnv4gzik"; |
||||
type = "gem"; |
||||
}; |
||||
version = "6.1.4.4"; |
||||
}; |
||||
activesupport = { |
||||
dependencies = ["concurrent-ruby" "i18n" "minitest" "tzinfo" "zeitwerk"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0rvnz9lsf9mrkpji748sf51f54m027snkw6rm8flyvf7fq18rm98"; |
||||
type = "gem"; |
||||
}; |
||||
version = "6.1.4.4"; |
||||
}; |
||||
bcrypt = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "02r1c3isfchs5fxivbq99gc3aq4vfyn8snhcy707dal1p8qz12qb"; |
||||
type = "gem"; |
||||
}; |
||||
version = "3.1.16"; |
||||
}; |
||||
charlock_holmes = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0hybw8jw9ryvz5zrki3gc9r88jqy373m6v46ynxsdzv1ysiyr40p"; |
||||
type = "gem"; |
||||
}; |
||||
version = "0.7.7"; |
||||
}; |
||||
concurrent-ruby = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0nwad3211p7yv9sda31jmbyw6sdafzmdi2i2niaz6f0wk5nq9h0f"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.1.9"; |
||||
}; |
||||
daemons = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "07cszb0zl8mqmwhc8a2yfg36vi6lbgrp4pa5bvmryrpcz9v6viwg"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.4.1"; |
||||
}; |
||||
eventmachine = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0wh9aqb0skz80fhfn66lbpr4f86ya2z5rx6gm5xlfhd05bj1ch4r"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.2.7"; |
||||
}; |
||||
gpgme = { |
||||
dependencies = ["mini_portile2"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0xbgh9d8nbvsvyzqnd0mzhz0nr9hx4qn025kmz6d837lry4lc6gw"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.0.20"; |
||||
}; |
||||
i18n = { |
||||
dependencies = ["concurrent-ruby"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0vdd1kii40qhbr9n8qx71k2gskq6rkl8ygy8hw5hfj8bb5a364xf"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.8.11"; |
||||
}; |
||||
mail = { |
||||
dependencies = ["mini_mime"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "00wwz6ys0502dpk8xprwcqfwyf3hmnx6lgxaiq6vj43mkx43sapc"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.7.1"; |
||||
}; |
||||
mail-gpg = { |
||||
dependencies = ["gpgme" "mail"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "1rz936m8nacy7agksvpvkf6b37d1h5qvh5xkrjqvv5wbdqs3cyfj"; |
||||
type = "gem"; |
||||
}; |
||||
version = "0.4.4"; |
||||
}; |
||||
mini_mime = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0lbim375gw2dk6383qirz13hgdmxlan0vc5da2l072j3qw6fqjm5"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.1.2"; |
||||
}; |
||||
mini_portile2 = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0d3ga166pahsxavzwj19yjj4lr13rw1vsb36s2qs8blcxigrdp6z"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.7.1"; |
||||
}; |
||||
minitest = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "06xf558gid4w8lwx13jwfdafsch9maz8m0g85wnfymqj63x5nbbd"; |
||||
type = "gem"; |
||||
}; |
||||
version = "5.15.0"; |
||||
}; |
||||
multi_json = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0pb1g1y3dsiahavspyzkdy39j4q377009f6ix0bh1ag4nqw43l0z"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.15.0"; |
||||
}; |
||||
mustermann = { |
||||
dependencies = ["ruby2_keywords"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0ccm54qgshr1lq3pr1dfh7gphkilc19dp63rw6fcx7460pjwy88a"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.1.1"; |
||||
}; |
||||
rack = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0i5vs0dph9i5jn8dfc6aqd6njcafmb20rwqngrf759c9cvmyff16"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.2.3"; |
||||
}; |
||||
rack-protection = { |
||||
dependencies = ["rack"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "159a4j4kragqh0z0z8vrpilpmaisnlz3n7kgiyf16bxkwlb3qlhz"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.1.0"; |
||||
}; |
||||
rake = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "15whn7p9nrkxangbs9hh75q585yfn66lv0v2mhj6q6dl6x8bzr2w"; |
||||
type = "gem"; |
||||
}; |
||||
version = "13.0.6"; |
||||
}; |
||||
ruby2_keywords = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "1vz322p8n39hz3b4a9gkmz9y7a5jaz41zrm2ywf31dvkqm03glgz"; |
||||
type = "gem"; |
||||
}; |
||||
version = "0.0.5"; |
||||
}; |
||||
schleuder = { |
||||
dependencies = ["activerecord" "bcrypt" "charlock_holmes" "gpgme" "mail" "mail-gpg" "rake" "sinatra" "sinatra-contrib" "sqlite3" "thin" "thor"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "15j1rfkfvni82msamikynsg48s50hbsx1pxm3y967caq9s80ll6c"; |
||||
type = "gem"; |
||||
}; |
||||
version = "4.0.2"; |
||||
}; |
||||
sinatra = { |
||||
dependencies = ["mustermann" "rack" "rack-protection" "tilt"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0dd53rzpkxgs697pycbhhgc9vcnxra4ly4xar8ni6aiydx2f88zk"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.1.0"; |
||||
}; |
||||
sinatra-contrib = { |
||||
dependencies = ["multi_json" "mustermann" "rack-protection" "sinatra" "tilt"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "1rl1iiafz51yzjd0vchl2lni7lmwppjql6cn1fnfxbma707qlcja"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.1.0"; |
||||
}; |
||||
sqlite3 = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0lja01cp9xd5m6vmx99zwn4r7s97r1w5cb76gqd8xhbm1wxyzf78"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.4.2"; |
||||
}; |
||||
thin = { |
||||
dependencies = ["daemons" "eventmachine" "rack"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "123bh7qlv6shk8bg8cjc84ix8bhlfcilwnn3iy6zq3l57yaplm9l"; |
||||
type = "gem"; |
||||
}; |
||||
version = "1.8.1"; |
||||
}; |
||||
thor = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "1yhrnp9x8qcy5vc7g438amd5j9sw83ih7c30dr6g6slgw9zj3g29"; |
||||
type = "gem"; |
||||
}; |
||||
version = "0.20.3"; |
||||
}; |
||||
tilt = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0rn8z8hda4h41a64l0zhkiwz2vxw9b1nb70gl37h1dg2k874yrlv"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.0.10"; |
||||
}; |
||||
tzinfo = { |
||||
dependencies = ["concurrent-ruby"]; |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "10qp5x7f9hvlc0psv9gsfbxg4a7s0485wsbq1kljkxq94in91l4z"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.0.4"; |
||||
}; |
||||
zeitwerk = { |
||||
groups = ["default"]; |
||||
platforms = []; |
||||
source = { |
||||
remotes = ["https://rubygems.org"]; |
||||
sha256 = "0lmg9x683gr9mkrbq9df2m0zb0650mdfxqna0bs10js44inv7znx"; |
||||
type = "gem"; |
||||
}; |
||||
version = "2.5.3"; |
||||
}; |
||||
} |
Loading…
Reference in new issue