From c10faa5b2b617d5d62407567cd8d495d7ddddeb6 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 15 Jun 2016 16:53:59 -0400 Subject: git mv bin scripts --- .gitignore | 2 +- bin/common.rb | 22 ----- bin/meta-cat | 6 -- bin/meta-check | 44 --------- bin/meta-normalize-stdio | 171 ----------------------------------- bin/pacman-make-keyring | 150 ------------------------------ bin/pgp-list-keyids | 21 ----- bin/postfix-generate-virtual-map | 18 ---- bin/ssh-list-authorized-keys | 25 ----- bin/uid-map | 8 -- scripts/common.rb | 22 +++++ scripts/meta-cat | 6 ++ scripts/meta-check | 44 +++++++++ scripts/meta-normalize-stdio | 171 +++++++++++++++++++++++++++++++++++ scripts/pacman-make-keyring | 150 ++++++++++++++++++++++++++++++ scripts/pgp-list-keyids | 21 +++++ scripts/postfix-generate-virtual-map | 18 ++++ scripts/ssh-list-authorized-keys | 25 +++++ scripts/uid-map | 8 ++ 19 files changed, 466 insertions(+), 466 deletions(-) delete mode 100644 bin/common.rb delete mode 100755 bin/meta-cat delete mode 100755 bin/meta-check delete mode 100755 bin/meta-normalize-stdio delete mode 100755 bin/pacman-make-keyring delete mode 100755 bin/pgp-list-keyids delete mode 100755 bin/postfix-generate-virtual-map delete mode 100755 bin/ssh-list-authorized-keys delete mode 100755 bin/uid-map create mode 100644 scripts/common.rb create mode 100755 scripts/meta-cat create mode 100755 scripts/meta-check create mode 100755 scripts/meta-normalize-stdio create mode 100755 scripts/pacman-make-keyring create mode 100755 scripts/pgp-list-keyids create mode 100755 scripts/postfix-generate-virtual-map create mode 100755 scripts/ssh-list-authorized-keys create mode 100755 scripts/uid-map diff --git a/.gitignore b/.gitignore index 27900c5..5408910 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /pkg -/bin/nshd +/bin/ /src/*.*/ /nshd.service /nshd.socket diff --git a/bin/common.rb b/bin/common.rb deleted file mode 100644 index 91e14be..0000000 --- a/bin/common.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'yaml' - -def cfg - @cfg ||= YAML::load(open("parabola-hackers.yml")) -end - -def load_user_yaml(filename) - user = YAML::load(open(filename)) - groups = user["groups"] || [] - user["groups"] = groups.concat((groups & cfg["groupgroups"].keys).map{|g|cfg["groupgroups"][g]}.flatten) - return user -end - -def load_all_users - users = {} - Dir.glob("#{cfg["yamldir"]}/*.yml").map{|filename| - uid = File.basename(filename).sub(/^([0-9]*)\.yml$/, "\\1").to_i - user = load_user_yaml(filename) - users[uid] = user - } - return users -end diff --git a/bin/meta-cat b/bin/meta-cat deleted file mode 100755 index e6b9edd..0000000 --- a/bin/meta-cat +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env ruby -# Usage: meta-cat - -load "#{File.dirname(__FILE__)}/common.rb" - -print load_all_users.to_yaml diff --git a/bin/meta-check b/bin/meta-check deleted file mode 100755 index 4a2981e..0000000 --- a/bin/meta-check +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -. libremessages - -mydir="$(dirname "$0")" -PATH="$mydir:$PATH" - -check-yaml() { - file=$1 - msg 'Inspecting %q' "$file" - norm=$(mktemp --tmpdir) - trap "rm -f -- $(printf '%q' "$norm")" RETURN - meta-normalize-stdio < "$file" > "$norm" || return $? - colordiff -u "$file" "$norm" || return $? -} - -main() { - declare -i ret=0 - - yamldir="$(ruby -e "load '$mydir/common.rb'; print cfg['yamldir']")" - - # Check the user YAML files - for file in "$yamldir"/*.yml; do - check-yaml "$file" || ret=$? - done - - msg 'Checking for duplicate usernames' - dups=($(sed -n 's/^username: //p' -- "$yamldir"/*.yml| sort | uniq -d)) - if (( ${#dups[@]} )); then - error 'Duplicate usernames:' - plain '%s' "${dups[@]}" - ret=1 - fi - - msg 'Checking PGP keys' - if pgp-list-keyids | grep -Ev '^(trusted|secondary|revoked)/[a-z][a-z0-9-]* [0-9A-F]{40}$'; then - error 'Bad pgp keys ^^^' - ret=1 - fi - - return $ret -} - -main "$@" diff --git a/bin/meta-normalize-stdio b/bin/meta-normalize-stdio deleted file mode 100755 index 5611ae6..0000000 --- a/bin/meta-normalize-stdio +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env ruby - -# First we define a bunch of code-generators, then at the end is a -# very neat and readable definition of the format of the YAML files. - -require 'yaml' - -def error(msg) - $stderr.puts "ERROR: #{msg}" - @err = 1 -end - -def warning(msg) - $stderr.puts "WARNING: #{msg}" -end - - -# Generic validators/formatters - -def semiordered_list(cnt, validator) - lambda {|name,ary| - if ary.class != Array - error "`#{name}' must be a list" - else - ary.each_index{|i| ary[i] = validator.call("#{name}[#{i}]", ary[i])} - ary = ary.first(cnt).concat(ary.last(ary.count-cnt).sort) - end - ary - } -end - -def unordered_list(validator) - semiordered_list(0, validator) -end - -def _unknown(map_name, key) - error "Unknown item: #{map_name}[#{key.inspect}]" - 0 -end -def unordered_map1(validator) - lambda {|name,hash| - if hash.class != Hash - error "`#{name}' must be a map" - else - order = Hash[[*validator.keys.map.with_index]] - hash = Hash[hash.sort_by{|k,v| order[k] || _unknown(name,k) }] - hash.keys.each{|k| - if validator[k] - hash[k] = validator[k].call("#{name}[#{k.inspect}]", hash[k]) - end - } - end - hash - } -end - -def unordered_map2(key_validator, val_validator) - lambda {|name,hash| - if hash.class != Hash - error "`#{name}' must be a map" - else - hash = Hash[hash.sort_by{|k,v| k}] - hash.keys.each{|k| - key_validator.call("#{name} key #{k.inspect}", k) - hash[k] = val_validator.call("#{name}[#{k.inspect}]", hash[k]) - } - end - hash - } -end - -string = lambda {|name,str| - if str.class != String - error "`#{name}' must be a string" - else - str - end -} - -# Regular Expression String -def restring(re) - lambda {|name,str| - if str.class != String - error "`#{name}' must be a string" - else - unless re =~ str - error "`#{name}' does not match #{re.inspect}: #{str}" - end - str - end - } -end - - -# Specific validators/formatters - -year = lambda {|name, num| - if num.class != Fixnum - error "`#{name}' must be a year" - else - if (num < 1900 || num > 3000) - error "`#{name}' is a number, but doesn't look like a year" - end - num - end -} - -# This regex is taken from http://www.w3.org/TR/html5/forms.html#valid-e-mail-address -_email_regex = /^[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ -email_list = lambda {|name, ary| - if ary.class != Array - error "`#{name}' must be a list" - elsif not ary.empty? - preserve = 1 - if ary.first.end_with?("@parabola.nu") and ary.count >= 2 - preserve = 2 - end - ary = semiordered_list(preserve, restring(_email_regex)).call(name, ary) - end - ary -} - -shell = lambda {|name, sh| - if sh.class != String - error "`#{name}' must be a string" - else - @valid_shells ||= open("/etc/shells").read.split("\n") - .find_all{|line| /^[^\#]/ =~ line} - unless @valid_shells.include?(sh) - warning "shell not listed in /etc/shells: #{sh}" - end - end - sh -} - - -# The format of the YAML files - -format = unordered_map1( - { - "username" => restring(/^[a-z][a-z0-9-]*$/), - "fullname" => string, - "email" => email_list, - "groups" => semiordered_list(1, string), - "pgp_keyid" => restring(/^[0-9A-F]{40}$/), - "pgp_revoked_keyids" => unordered_list(restring(/^[0-9A-F]{40}$/)), - "ssh_keys" => unordered_map2(string, string), - "shell" => shell, - "extra" => unordered_map1( - { - "alias" => string, - "other_contact" => string, - "roles" => string, - "website" => string, - "occupation" => string, - "yob" => year, - "location" => string, - "languages" => string, - "interests" => string, - "favorite_distros" => string, - }) - }) - - - -@err = 0 -user = format.call("user", YAML::load(STDIN)) -if @err != 0 - exit @err -end -print user.to_yaml diff --git a/bin/pacman-make-keyring b/bin/pacman-make-keyring deleted file mode 100755 index 589984d..0000000 --- a/bin/pacman-make-keyring +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/make -rRf -# Usage: pacman-make-keyring V=$(date -u +%Y%m%d) -ifeq ($(origin V),undefined) -$(info Usage: pacman-make-keyring V=$$(date -u +%Y%m%d)) -$(error You must set V= on the command line) -endif - -bin := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) -yamldir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['yamldir']") -cachedir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['keyring_cachedir']") - -outputdir = $(cachedir)/$(KEYRING_NAME)-keyring-$(V) -KEYRING_NAME = parabola - -all: $(KEYRING_NAME)-keyring-$(V).tar.gz -.PHONY: all - -export SHELL = /bin/bash -o pipefail -.PHONY: FORCE -.SECONDARY: -.DELETE_ON_ERROR: - -dirs = \ - $(outputdir) \ - $(cachedir) \ - $(cachedir)/gpghome \ - $(cachedir)/keys/trusted \ - $(cachedir)/keys/secondary \ - $(cachedir)/keys/revoked - -$(dirs): - mkdir -p $@ - -$(cachedir)/var.%: FORCE | $(cachedir) - @$(file >$(@D)/tmp.$(@F),$($*)) - @sed -i 's|^|#|' $(@D)/tmp.$(@F) - @if cmp -s $(@D)/tmp.$(@F) $@; then \ - rm -f $(@D)/tmp.$(@F) || :; \ - else \ - mv -f $(@D)/tmp.$(@F) $@; \ - fi --include $(wildcard $(cachedir)/var.*) -$(cachedir)/txt.%: $(cachedir)/var.% - sed 's|^#||' < $< > $@ -var=$(cachedir)/var. - -keyring-files = \ - $(outputdir)/Makefile \ - $(outputdir)/${KEYRING_NAME}.gpg \ - $(outputdir)/${KEYRING_NAME}-trusted \ - $(outputdir)/${KEYRING_NAME}-revoked - -$(KEYRING_NAME)-keyring-$(V).tar.gz: %.tar.gz: $(keyring-files) - bsdtar --format=ustar -cf - -C $(cachedir) $(addprefix $*/,$(notdir $^)) | gzip -9 > $@ - -define Makefile.in -V=@V@ - -prefix = /usr/local -PREFIX = $$(prefix) - -install: - install -dm755 $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ - install -m0644 @KEYRING_NAME@{.gpg,-trusted,-revoked} $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ - -uninstall: - rm -f $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/@KEYRING_NAME@{.gpg,-trusted,-revoked} - rmdir -p --ignore-fail-on-non-empty $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ - -.PHONY: install uninstall -endef - -$(outputdir)/Makefile: $(cachedir)/txt.Makefile.in $(var)V $(var)KEYRING_NAME | $(outputdir) - sed $(foreach v,$(patsubst $(var)%,%,$(filter $(var)%,$^)), -e 's|@$v@|$($v)|' ) < $< > $@ - - -users := $(sort $(shell find $(yamldir))) $(var)users - -# Assemble the list of .asc files needed to generate the keyring -$(cachedir)/deps.mk: ${users} $(var)outputdir $(var)cachedir $(var)KEYRING_NAME| $(cachedir) - { \ - echo $(outputdir)/${KEYRING_NAME}.gpg: $$($(bin)/pgp-list-keyids | sed -r 's|(\S+) .*|$$(cachedir)/keys/\1.asc|') && \ - echo $(cachedir)/stamp.ownertrust: $$($(bin)/pgp-list-keyids | sed -rn 's|^(trusted/\S+) .*|$$(cachedir)/keys/\1.asc|p') && \ - $(bin)/pgp-list-keyids | sed -rn 's|^trusted/(\S+) (.*)|keyid.\1 = \2|p' && \ - $(bin)/uid-map | sed 's|.*|trusted:&\nsecondary:&\nrevoked:&|' | sed -r 's|(.*):(.*):(.*)|$$(cachedir)/keys/\1/\3.asc: $$(yamldir)/\2.yml|' && \ - :; }> $@ --include $(cachedir)/deps.mk - -# The remainder of file is mostly just a translation of the shell -# script `update-keys`. -# -# https://git.archlinux.org/archlinux-keyring.git/tree/update-keys - -export LANG=C - -KEYSERVER = hkp://pool.sks-keyservers.net - -GPG = gpg --quiet --batch --no-tty --no-permission-warning --keyserver ${KEYSERVER} --homedir $(cachedir)/gpghome - -define gpg-init -%echo Generating Parabola Keyring keychain master key... -Key-Type: RSA -Key-Length: 1024 -Key-Usage: sign -Name-Real: Parabola Keyring Keychain Master Key -Name-Email: parabola-keyring@localhost -Expire-Date: 0 -%no-protection -%commit -%echo Done -endef -$(cachedir)/stamp.gpg-init: $(cachedir)/txt.gpg-init $(var)GPG | $(cachedir)/gpghome - ${GPG} --gen-key < $< - touch $@ - -# The appropriate ${uid}.yml file is added as a dependency to -# ${username}.yml by deps.mk -keyid=$(keyid.$(patsubst %.asc,%,$(notdir $@))) - -# In 'update-keys', this is the 'master-keyids' loop -$(outputdir)/${KEYRING_NAME}-trusted: ${users} | $(outputdir) - $(bin)/pgp-list-keyids | sed -rn 's|^trusted/\S+ (\S+)|\1:4:|p' > $@ -$(cachedir)/keys/trusted/%.asc : $(cachedir)/stamp.gpg-init | $(cachedir)/keys/trusted - ${GPG} --recv-keys ${keyid} &>/dev/null - printf 'minimize\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} - printf 'y\ny\n' | ${GPG} --command-fd 0 --lsign-key ${keyid} &>/dev/null - ${GPG} --armor --no-emit-version --export ${keyid} > $@ - -$(cachedir)/stamp.ownertrust: $(outputdir)/${KEYRING_NAME}-trusted $(cachedir)/deps.mk - ${GPG} --import-ownertrust < $< 2>/dev/null - touch $@ - -# In 'update-keys', this is the 'packager-keyids' loop -$(cachedir)/keys/secondary/%.asc: $(cachedir)/stamp.ownertrust | $(cachedir)/keys/secondary - ${GPG} --recv-keys ${keyid} &>/dev/null - printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} - ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it is trusted - ${GPG} --armor --no-emit-version --export ${keyid} > $@ - -# In 'update-keys', this is the 'packager-revoked-keyids' loop -$(outputdir)/${KEYRING_NAME}-revoked: ${users} | $(outputdir) - $(bin)/pgp-list-keyids | sed -rn 's|^revoked/\S+ ||p' > $@ -$(cachedir)/keys/revoked/%.asc : $(cachedir)/stamp.ownertrust | $(cachedir)/keys/revoked - ${GPG} --recv-keys ${keyid} &>/dev/null - printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} - ! ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it isn't trusted - ${GPG} --armor --no-emit-version --export ${keyid} > $@ - -$(outputdir)/${KEYRING_NAME}.gpg: $(cachedir)/deps.mk | $(outputdir) - cat $(filter %.asc,$^) > $@ diff --git a/bin/pgp-list-keyids b/bin/pgp-list-keyids deleted file mode 100755 index 9682b1a..0000000 --- a/bin/pgp-list-keyids +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env ruby -# Usage: pgp-list-keyids - -load "#{File.dirname(__FILE__)}/common.rb" - -load_all_users.each do |uid,user| - if user["groups"] - if user["groups"].include?("keyring-trusted") - puts "trusted/#{user["username"]} #{user["pgp_keyid"]}" - elsif user["groups"].include?("keyring-secondary") - puts "secondary/#{user["username"]} #{user["pgp_keyid"]}" - elsif user["pgp_keyid"] - #puts "revoked/#{user["username"]} #{user["pgp_keyid"]}" - end - end - if user["pgp_revoked_keyids"] - user["pgp_revoked_keyids"].each do |keyid| - puts "revoked/#{user["username"]} #{keyid}" - end - end -end diff --git a/bin/postfix-generate-virtual-map b/bin/postfix-generate-virtual-map deleted file mode 100755 index d5c2d21..0000000 --- a/bin/postfix-generate-virtual-map +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env ruby -# Usage: postfix-show-virtual-map > ${file} && postmap hash:${file} - -load "#{File.dirname(__FILE__)}/common.rb" - -users = load_all_users.values.find_all{|u|u["groups"].include?("email")} - -users.each do |user| - if user["email"] and user["email"].length > 0 - if user["email"][0] =~ /.*@parabola.nu$/ - if user["email"].length > 1 - puts "#{user["username"]}@parabola.nu #{user["email"][1]}" - end - else - puts "#{user["username"]}@parabola.nu #{user["email"][0]}" - end - end -end diff --git a/bin/ssh-list-authorized-keys b/bin/ssh-list-authorized-keys deleted file mode 100755 index 5fb1ea1..0000000 --- a/bin/ssh-list-authorized-keys +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env ruby -# Usage: ssh-list-authorized-keys [username] - -load "#{File.dirname(__FILE__)}/common.rb" - -all_users = load_all_users.values - -groupnames = ARGV & cfg["ssh_pseudo_users"] -usernames = ARGV & all_users.map{|u|u["username"]} - -users = all_users.find_all{|u| - # [ username was listed ] or [ the user is in a listed group ] - usernames.include?(u["username"]) or not (u["groups"] & groupnames).empty? -} - -# Buffer the output to avoid EPIPE when the reader hangs up early -output="" -users.each do |user| - if user["ssh_keys"] - user["ssh_keys"].each do |addr,key| - output+="#{key} #{user["fullname"]} (#{user["username"]}) <#{addr}>\n" - end - end -end -print output diff --git a/bin/uid-map b/bin/uid-map deleted file mode 100755 index 10c3fac..0000000 --- a/bin/uid-map +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby -# Usage: uid-map - -load "#{File.dirname(__FILE__)}/common.rb" - -load_all_users.each do |uid,user| - puts "#{uid}:#{user["username"]}" -end diff --git a/scripts/common.rb b/scripts/common.rb new file mode 100644 index 0000000..91e14be --- /dev/null +++ b/scripts/common.rb @@ -0,0 +1,22 @@ +require 'yaml' + +def cfg + @cfg ||= YAML::load(open("parabola-hackers.yml")) +end + +def load_user_yaml(filename) + user = YAML::load(open(filename)) + groups = user["groups"] || [] + user["groups"] = groups.concat((groups & cfg["groupgroups"].keys).map{|g|cfg["groupgroups"][g]}.flatten) + return user +end + +def load_all_users + users = {} + Dir.glob("#{cfg["yamldir"]}/*.yml").map{|filename| + uid = File.basename(filename).sub(/^([0-9]*)\.yml$/, "\\1").to_i + user = load_user_yaml(filename) + users[uid] = user + } + return users +end diff --git a/scripts/meta-cat b/scripts/meta-cat new file mode 100755 index 0000000..e6b9edd --- /dev/null +++ b/scripts/meta-cat @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# Usage: meta-cat + +load "#{File.dirname(__FILE__)}/common.rb" + +print load_all_users.to_yaml diff --git a/scripts/meta-check b/scripts/meta-check new file mode 100755 index 0000000..4a2981e --- /dev/null +++ b/scripts/meta-check @@ -0,0 +1,44 @@ +#!/bin/bash + +. libremessages + +mydir="$(dirname "$0")" +PATH="$mydir:$PATH" + +check-yaml() { + file=$1 + msg 'Inspecting %q' "$file" + norm=$(mktemp --tmpdir) + trap "rm -f -- $(printf '%q' "$norm")" RETURN + meta-normalize-stdio < "$file" > "$norm" || return $? + colordiff -u "$file" "$norm" || return $? +} + +main() { + declare -i ret=0 + + yamldir="$(ruby -e "load '$mydir/common.rb'; print cfg['yamldir']")" + + # Check the user YAML files + for file in "$yamldir"/*.yml; do + check-yaml "$file" || ret=$? + done + + msg 'Checking for duplicate usernames' + dups=($(sed -n 's/^username: //p' -- "$yamldir"/*.yml| sort | uniq -d)) + if (( ${#dups[@]} )); then + error 'Duplicate usernames:' + plain '%s' "${dups[@]}" + ret=1 + fi + + msg 'Checking PGP keys' + if pgp-list-keyids | grep -Ev '^(trusted|secondary|revoked)/[a-z][a-z0-9-]* [0-9A-F]{40}$'; then + error 'Bad pgp keys ^^^' + ret=1 + fi + + return $ret +} + +main "$@" diff --git a/scripts/meta-normalize-stdio b/scripts/meta-normalize-stdio new file mode 100755 index 0000000..5611ae6 --- /dev/null +++ b/scripts/meta-normalize-stdio @@ -0,0 +1,171 @@ +#!/usr/bin/env ruby + +# First we define a bunch of code-generators, then at the end is a +# very neat and readable definition of the format of the YAML files. + +require 'yaml' + +def error(msg) + $stderr.puts "ERROR: #{msg}" + @err = 1 +end + +def warning(msg) + $stderr.puts "WARNING: #{msg}" +end + + +# Generic validators/formatters + +def semiordered_list(cnt, validator) + lambda {|name,ary| + if ary.class != Array + error "`#{name}' must be a list" + else + ary.each_index{|i| ary[i] = validator.call("#{name}[#{i}]", ary[i])} + ary = ary.first(cnt).concat(ary.last(ary.count-cnt).sort) + end + ary + } +end + +def unordered_list(validator) + semiordered_list(0, validator) +end + +def _unknown(map_name, key) + error "Unknown item: #{map_name}[#{key.inspect}]" + 0 +end +def unordered_map1(validator) + lambda {|name,hash| + if hash.class != Hash + error "`#{name}' must be a map" + else + order = Hash[[*validator.keys.map.with_index]] + hash = Hash[hash.sort_by{|k,v| order[k] || _unknown(name,k) }] + hash.keys.each{|k| + if validator[k] + hash[k] = validator[k].call("#{name}[#{k.inspect}]", hash[k]) + end + } + end + hash + } +end + +def unordered_map2(key_validator, val_validator) + lambda {|name,hash| + if hash.class != Hash + error "`#{name}' must be a map" + else + hash = Hash[hash.sort_by{|k,v| k}] + hash.keys.each{|k| + key_validator.call("#{name} key #{k.inspect}", k) + hash[k] = val_validator.call("#{name}[#{k.inspect}]", hash[k]) + } + end + hash + } +end + +string = lambda {|name,str| + if str.class != String + error "`#{name}' must be a string" + else + str + end +} + +# Regular Expression String +def restring(re) + lambda {|name,str| + if str.class != String + error "`#{name}' must be a string" + else + unless re =~ str + error "`#{name}' does not match #{re.inspect}: #{str}" + end + str + end + } +end + + +# Specific validators/formatters + +year = lambda {|name, num| + if num.class != Fixnum + error "`#{name}' must be a year" + else + if (num < 1900 || num > 3000) + error "`#{name}' is a number, but doesn't look like a year" + end + num + end +} + +# This regex is taken from http://www.w3.org/TR/html5/forms.html#valid-e-mail-address +_email_regex = /^[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ +email_list = lambda {|name, ary| + if ary.class != Array + error "`#{name}' must be a list" + elsif not ary.empty? + preserve = 1 + if ary.first.end_with?("@parabola.nu") and ary.count >= 2 + preserve = 2 + end + ary = semiordered_list(preserve, restring(_email_regex)).call(name, ary) + end + ary +} + +shell = lambda {|name, sh| + if sh.class != String + error "`#{name}' must be a string" + else + @valid_shells ||= open("/etc/shells").read.split("\n") + .find_all{|line| /^[^\#]/ =~ line} + unless @valid_shells.include?(sh) + warning "shell not listed in /etc/shells: #{sh}" + end + end + sh +} + + +# The format of the YAML files + +format = unordered_map1( + { + "username" => restring(/^[a-z][a-z0-9-]*$/), + "fullname" => string, + "email" => email_list, + "groups" => semiordered_list(1, string), + "pgp_keyid" => restring(/^[0-9A-F]{40}$/), + "pgp_revoked_keyids" => unordered_list(restring(/^[0-9A-F]{40}$/)), + "ssh_keys" => unordered_map2(string, string), + "shell" => shell, + "extra" => unordered_map1( + { + "alias" => string, + "other_contact" => string, + "roles" => string, + "website" => string, + "occupation" => string, + "yob" => year, + "location" => string, + "languages" => string, + "interests" => string, + "favorite_distros" => string, + }) + }) + + + +@err = 0 +user = format.call("user", YAML::load(STDIN)) +if @err != 0 + exit @err +end +print user.to_yaml diff --git a/scripts/pacman-make-keyring b/scripts/pacman-make-keyring new file mode 100755 index 0000000..589984d --- /dev/null +++ b/scripts/pacman-make-keyring @@ -0,0 +1,150 @@ +#!/usr/bin/make -rRf +# Usage: pacman-make-keyring V=$(date -u +%Y%m%d) +ifeq ($(origin V),undefined) +$(info Usage: pacman-make-keyring V=$$(date -u +%Y%m%d)) +$(error You must set V= on the command line) +endif + +bin := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) +yamldir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['yamldir']") +cachedir := $(shell ruby -e "load '$(bin)/common.rb'; print cfg['keyring_cachedir']") + +outputdir = $(cachedir)/$(KEYRING_NAME)-keyring-$(V) +KEYRING_NAME = parabola + +all: $(KEYRING_NAME)-keyring-$(V).tar.gz +.PHONY: all + +export SHELL = /bin/bash -o pipefail +.PHONY: FORCE +.SECONDARY: +.DELETE_ON_ERROR: + +dirs = \ + $(outputdir) \ + $(cachedir) \ + $(cachedir)/gpghome \ + $(cachedir)/keys/trusted \ + $(cachedir)/keys/secondary \ + $(cachedir)/keys/revoked + +$(dirs): + mkdir -p $@ + +$(cachedir)/var.%: FORCE | $(cachedir) + @$(file >$(@D)/tmp.$(@F),$($*)) + @sed -i 's|^|#|' $(@D)/tmp.$(@F) + @if cmp -s $(@D)/tmp.$(@F) $@; then \ + rm -f $(@D)/tmp.$(@F) || :; \ + else \ + mv -f $(@D)/tmp.$(@F) $@; \ + fi +-include $(wildcard $(cachedir)/var.*) +$(cachedir)/txt.%: $(cachedir)/var.% + sed 's|^#||' < $< > $@ +var=$(cachedir)/var. + +keyring-files = \ + $(outputdir)/Makefile \ + $(outputdir)/${KEYRING_NAME}.gpg \ + $(outputdir)/${KEYRING_NAME}-trusted \ + $(outputdir)/${KEYRING_NAME}-revoked + +$(KEYRING_NAME)-keyring-$(V).tar.gz: %.tar.gz: $(keyring-files) + bsdtar --format=ustar -cf - -C $(cachedir) $(addprefix $*/,$(notdir $^)) | gzip -9 > $@ + +define Makefile.in +V=@V@ + +prefix = /usr/local +PREFIX = $$(prefix) + +install: + install -dm755 $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ + install -m0644 @KEYRING_NAME@{.gpg,-trusted,-revoked} $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ + +uninstall: + rm -f $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/@KEYRING_NAME@{.gpg,-trusted,-revoked} + rmdir -p --ignore-fail-on-non-empty $$(DESTDIR)$$(PREFIX)/share/pacman/keyrings/ + +.PHONY: install uninstall +endef + +$(outputdir)/Makefile: $(cachedir)/txt.Makefile.in $(var)V $(var)KEYRING_NAME | $(outputdir) + sed $(foreach v,$(patsubst $(var)%,%,$(filter $(var)%,$^)), -e 's|@$v@|$($v)|' ) < $< > $@ + + +users := $(sort $(shell find $(yamldir))) $(var)users + +# Assemble the list of .asc files needed to generate the keyring +$(cachedir)/deps.mk: ${users} $(var)outputdir $(var)cachedir $(var)KEYRING_NAME| $(cachedir) + { \ + echo $(outputdir)/${KEYRING_NAME}.gpg: $$($(bin)/pgp-list-keyids | sed -r 's|(\S+) .*|$$(cachedir)/keys/\1.asc|') && \ + echo $(cachedir)/stamp.ownertrust: $$($(bin)/pgp-list-keyids | sed -rn 's|^(trusted/\S+) .*|$$(cachedir)/keys/\1.asc|p') && \ + $(bin)/pgp-list-keyids | sed -rn 's|^trusted/(\S+) (.*)|keyid.\1 = \2|p' && \ + $(bin)/uid-map | sed 's|.*|trusted:&\nsecondary:&\nrevoked:&|' | sed -r 's|(.*):(.*):(.*)|$$(cachedir)/keys/\1/\3.asc: $$(yamldir)/\2.yml|' && \ + :; }> $@ +-include $(cachedir)/deps.mk + +# The remainder of file is mostly just a translation of the shell +# script `update-keys`. +# +# https://git.archlinux.org/archlinux-keyring.git/tree/update-keys + +export LANG=C + +KEYSERVER = hkp://pool.sks-keyservers.net + +GPG = gpg --quiet --batch --no-tty --no-permission-warning --keyserver ${KEYSERVER} --homedir $(cachedir)/gpghome + +define gpg-init +%echo Generating Parabola Keyring keychain master key... +Key-Type: RSA +Key-Length: 1024 +Key-Usage: sign +Name-Real: Parabola Keyring Keychain Master Key +Name-Email: parabola-keyring@localhost +Expire-Date: 0 +%no-protection +%commit +%echo Done +endef +$(cachedir)/stamp.gpg-init: $(cachedir)/txt.gpg-init $(var)GPG | $(cachedir)/gpghome + ${GPG} --gen-key < $< + touch $@ + +# The appropriate ${uid}.yml file is added as a dependency to +# ${username}.yml by deps.mk +keyid=$(keyid.$(patsubst %.asc,%,$(notdir $@))) + +# In 'update-keys', this is the 'master-keyids' loop +$(outputdir)/${KEYRING_NAME}-trusted: ${users} | $(outputdir) + $(bin)/pgp-list-keyids | sed -rn 's|^trusted/\S+ (\S+)|\1:4:|p' > $@ +$(cachedir)/keys/trusted/%.asc : $(cachedir)/stamp.gpg-init | $(cachedir)/keys/trusted + ${GPG} --recv-keys ${keyid} &>/dev/null + printf 'minimize\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} + printf 'y\ny\n' | ${GPG} --command-fd 0 --lsign-key ${keyid} &>/dev/null + ${GPG} --armor --no-emit-version --export ${keyid} > $@ + +$(cachedir)/stamp.ownertrust: $(outputdir)/${KEYRING_NAME}-trusted $(cachedir)/deps.mk + ${GPG} --import-ownertrust < $< 2>/dev/null + touch $@ + +# In 'update-keys', this is the 'packager-keyids' loop +$(cachedir)/keys/secondary/%.asc: $(cachedir)/stamp.ownertrust | $(cachedir)/keys/secondary + ${GPG} --recv-keys ${keyid} &>/dev/null + printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} + ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it is trusted + ${GPG} --armor --no-emit-version --export ${keyid} > $@ + +# In 'update-keys', this is the 'packager-revoked-keyids' loop +$(outputdir)/${KEYRING_NAME}-revoked: ${users} | $(outputdir) + $(bin)/pgp-list-keyids | sed -rn 's|^revoked/\S+ ||p' > $@ +$(cachedir)/keys/revoked/%.asc : $(cachedir)/stamp.ownertrust | $(cachedir)/keys/revoked + ${GPG} --recv-keys ${keyid} &>/dev/null + printf 'clean\nquit\ny\n' | ${GPG} --command-fd 0 --edit-key ${keyid} + ! ${GPG} --list-keys --with-colons ${keyid} 2>/dev/null | grep -q '^pub:f:' # make sure it isn't trusted + ${GPG} --armor --no-emit-version --export ${keyid} > $@ + +$(outputdir)/${KEYRING_NAME}.gpg: $(cachedir)/deps.mk | $(outputdir) + cat $(filter %.asc,$^) > $@ diff --git a/scripts/pgp-list-keyids b/scripts/pgp-list-keyids new file mode 100755 index 0000000..9682b1a --- /dev/null +++ b/scripts/pgp-list-keyids @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby +# Usage: pgp-list-keyids + +load "#{File.dirname(__FILE__)}/common.rb" + +load_all_users.each do |uid,user| + if user["groups"] + if user["groups"].include?("keyring-trusted") + puts "trusted/#{user["username"]} #{user["pgp_keyid"]}" + elsif user["groups"].include?("keyring-secondary") + puts "secondary/#{user["username"]} #{user["pgp_keyid"]}" + elsif user["pgp_keyid"] + #puts "revoked/#{user["username"]} #{user["pgp_keyid"]}" + end + end + if user["pgp_revoked_keyids"] + user["pgp_revoked_keyids"].each do |keyid| + puts "revoked/#{user["username"]} #{keyid}" + end + end +end diff --git a/scripts/postfix-generate-virtual-map b/scripts/postfix-generate-virtual-map new file mode 100755 index 0000000..d5c2d21 --- /dev/null +++ b/scripts/postfix-generate-virtual-map @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby +# Usage: postfix-show-virtual-map > ${file} && postmap hash:${file} + +load "#{File.dirname(__FILE__)}/common.rb" + +users = load_all_users.values.find_all{|u|u["groups"].include?("email")} + +users.each do |user| + if user["email"] and user["email"].length > 0 + if user["email"][0] =~ /.*@parabola.nu$/ + if user["email"].length > 1 + puts "#{user["username"]}@parabola.nu #{user["email"][1]}" + end + else + puts "#{user["username"]}@parabola.nu #{user["email"][0]}" + end + end +end diff --git a/scripts/ssh-list-authorized-keys b/scripts/ssh-list-authorized-keys new file mode 100755 index 0000000..5fb1ea1 --- /dev/null +++ b/scripts/ssh-list-authorized-keys @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby +# Usage: ssh-list-authorized-keys [username] + +load "#{File.dirname(__FILE__)}/common.rb" + +all_users = load_all_users.values + +groupnames = ARGV & cfg["ssh_pseudo_users"] +usernames = ARGV & all_users.map{|u|u["username"]} + +users = all_users.find_all{|u| + # [ username was listed ] or [ the user is in a listed group ] + usernames.include?(u["username"]) or not (u["groups"] & groupnames).empty? +} + +# Buffer the output to avoid EPIPE when the reader hangs up early +output="" +users.each do |user| + if user["ssh_keys"] + user["ssh_keys"].each do |addr,key| + output+="#{key} #{user["fullname"]} (#{user["username"]}) <#{addr}>\n" + end + end +end +print output diff --git a/scripts/uid-map b/scripts/uid-map new file mode 100755 index 0000000..10c3fac --- /dev/null +++ b/scripts/uid-map @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +# Usage: uid-map + +load "#{File.dirname(__FILE__)}/common.rb" + +load_all_users.each do |uid,user| + puts "#{uid}:#{user["username"]}" +end -- cgit v1.2.2