diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e992550..0000000 --- a/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -dist/* -propellor -tags -privdata/local -privdata/keyring.gpg~ -Setup -Setup.hi -Setup.o -docker diff --git a/Makefile b/Makefile index 7985268..d6e8fe6 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ CABAL?=cabal +DEBDEPS=gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev libghc-mtl-dev libghc-monadcatchio-transformers-dev + run: deps build ./propellor @@ -10,7 +12,7 @@ build: dist/setup-config ln -sf dist/build/propellor-config/propellor-config propellor deps: - @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-quickcheck2-dev libghc-mtl-dev libghc-monadcatchio-transformers-dev; fi || true + @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install $(DEBDEPS) || (apt-get update && apt-get --no-upgrade --no-install-recommends -y install $(DEBDEPS)); fi || true @if [ $$(whoami) = root ]; then apt-get --no-upgrade --no-install-recommends -y install libghc-async-dev || (cabal update; cabal install async); fi || true dist/setup-config: propellor.cabal diff --git a/config-joey.hs b/config-joey.hs index 2c73b3e..18f0a32 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -21,8 +21,10 @@ import qualified Propellor.Property.Git as Git import qualified Propellor.Property.Apache as Apache import qualified Propellor.Property.Postfix as Postfix import qualified Propellor.Property.Service as Service +import qualified Propellor.Property.Grub as Grub import qualified Propellor.Property.HostingProvider.DigitalOcean as DigitalOcean import qualified Propellor.Property.HostingProvider.CloudAtCost as CloudAtCost +import qualified Propellor.Property.HostingProvider.Linode as Linode import qualified Propellor.Property.SiteSpecific.GitHome as GitHome import qualified Propellor.Property.SiteSpecific.GitAnnexBuilder as GitAnnexBuilder import qualified Propellor.Property.SiteSpecific.JoeySites as JoeySites @@ -34,7 +36,6 @@ main = defaultMain hosts -- / \___-=O`/|O`/__| (____.' Deployed -} -- `/-==__ _/__|/__=-| ( \_ hosts :: [Host] -- * \ | | '--------' hosts = -- (o) ` - -- My laptop [ host "darkstar.kitenet.net" & ipv6 "2001:4830:1600:187::2" -- sixxs tunnel @@ -42,52 +43,22 @@ hosts = -- (o) ` & Docker.configured & Docker.docked hosts "android-git-annex" - -- Nothing super-important lives here and mostly it's docker containers. , standardSystem "clam.kitenet.net" Unstable "amd64" - & ipv4 "162.248.143.249" - & ipv6 "2002:5044:5531::1" + [ "Unreliable server. Anything here may be lost at any time!" ] + & ipv4 "162.248.9.29" & CloudAtCost.decruft & Apt.unattendedUpgrades & Network.ipv6to4 & Tor.isBridge & Postfix.satellite + & Docker.configured - - & Docker.docked hosts "oldusenet-shellbox" - & Docker.docked hosts "openid-provider" - `requires` Apt.serviceInstalledRunning "ntp" - & Docker.docked hosts "ancient-kitenet" - - -- I'd rather this were on diatom, but it needs unstable. - & alias "kgb.kitenet.net" - & JoeySites.kgbServer - - & alias "mumble.kitenet.net" - & JoeySites.mumbleServer hosts - - & alias "ns9.kitenet.net" - & myDnsSecondary - - & alias "znc.kitenet.net" - & JoeySites.ircBouncer - - -- For https port 443, shellinabox with ssh login to - -- kitenet.net - & alias "shell.kitenet.net" - & JoeySites.kiteShellBox - - -- Nothing is using http port 80 on clam, so listen on - -- that port for ssh, for traveling on bad networks that - -- block 22. - & "/etc/ssh/sshd_config" `File.containsLine` "Port 80" - `onChange` Service.restarted "ssh" - & Docker.garbageCollected `period` Daily - & Apt.installed ["git-annex", "mtr", "screen"] -- Orca is the main git-annex build box. , standardSystem "orca.kitenet.net" Unstable "amd64" + [ "Main git-annex build box." ] & ipv4 "138.38.108.179" & Hostname.sane @@ -102,15 +73,27 @@ hosts = -- (o) ` & Docker.garbageCollected `period` Daily & Apt.buildDep ["git-annex"] `period` Daily - -- Important stuff that needs not too much memory or CPU. + , standardSystem "kite.kitenet.net" Unstable "amd64" + [ "Welcome to the new kitenet.net server!" + , "This is still under construction and not yet live.." + ] + & ipv4 "66.228.36.95" + & ipv6 "2600:3c03::f03c:91ff:fe73:b0d2" + + & Apt.installed ["linux-image-amd64"] + & Linode.chainPVGrub 5 + & Hostname.sane + & Apt.unattendedUpgrades + & Apt.installed ["systemd"] + & Ssh.hostKeys (Context "kitenet.net") + , standardSystem "diatom.kitenet.net" Stable "amd64" + [ "Important stuff that needs not too much memory or CPU." ] & ipv4 "107.170.31.195" & DigitalOcean.distroKernel & Hostname.sane - & Ssh.hostKey SshDsa - & Ssh.hostKey SshRsa - & Ssh.hostKey SshEcdsa + & Ssh.hostKeys (Context "diatom.kitenet.net") & Apt.unattendedUpgrades & Apt.serviceInstalledRunning "ntp" & Postfix.satellite @@ -120,9 +103,9 @@ hosts = -- (o) ` & Apt.serviceInstalledRunning "swapspace" & Apt.serviceInstalledRunning "apache2" - & File.hasPrivContent "/etc/ssl/certs/web.pem" - & File.hasPrivContent "/etc/ssl/private/web.pem" - & File.hasPrivContent "/etc/ssl/certs/startssl.pem" + & File.hasPrivContent "/etc/ssl/certs/web.pem" (Context "kitenet.net") + & File.hasPrivContent "/etc/ssl/private/web.pem" (Context "kitenet.net") + & File.hasPrivContent "/etc/ssl/certs/startssl.pem" (Context "kitenet.net") & Apache.modEnabled "ssl" & Apache.multiSSL & File.ownerGroup "/srv/web" "joey" "joey" @@ -163,34 +146,18 @@ hosts = -- (o) ` & Dns.secondaryFor ["animx"] hosts "animx.eu.org" - -- storage and backup server - , standardSystem "elephant.kitenet.net" Unstable "amd64" + , let ctx = Context "elephant.kitenet.net" + in standardSystem "elephant.kitenet.net" Unstable "amd64" + [ "Storage, big data, and backups, omnomnom!" ] & ipv4 "193.234.225.114" + & Grub.chainPVGrub "hd0,0" "xen/xvda1" 30 & Hostname.sane & Postfix.satellite & Apt.unattendedUpgrades - & Ssh.hostKey SshDsa - & Ssh.hostKey SshRsa - & Ssh.hostKey SshEcdsa - & Ssh.keyImported SshRsa "joey" - - -- PV-grub chaining - -- http://notes.pault.ag/linode-pv-grub-chainning/ - -- (Adapted to use xvda1/hd0,0 instead of xvda/hd0) - & "/boot/grub/menu.lst" `File.hasContent` - [ "default 1" - , "timeout 30" - , "" - , "title grub-xen shim" - , "root (hd0,0)" - , "kernel /boot/xen-shim" - , "boot" - ] - & "/boot/load.cf" `File.hasContent` - [ "configfile (xen/xvda1)/boot/grub/grub.cfg" ] - & Apt.installed ["grub-xen"] - & flagFile (scriptProperty ["update-grub; grub-mkimage --prefix '(xen/xvda1)/boot/grub' -c /boot/load.cf -O x86_64-xen /usr/lib/grub/x86_64-xen/*.mod > /boot/xen-shim"]) "/boot/xen-shim" + & Ssh.hostKeys ctx + & Ssh.keyImported SshRsa "joey" ctx + & Apt.serviceInstalledRunning "swapspace" & alias "eubackup.kitenet.net" & Apt.installed ["obnam", "sshfs", "rsync"] @@ -201,8 +168,43 @@ hosts = -- (o) ` & alias "podcatcher.kitenet.net" & Apt.installed ["git-annex"] + & alias "znc.kitenet.net" + & JoeySites.ircBouncer + + -- I'd rather this were on diatom, but it needs unstable. + & alias "kgb.kitenet.net" + & JoeySites.kgbServer + + & alias "mumble.kitenet.net" + & JoeySites.mumbleServer hosts + + & alias "ns3.kitenet.net" + & myDnsSecondary + & Docker.configured + + & Docker.docked hosts "oldusenet-shellbox" + & Docker.docked hosts "openid-provider" + `requires` Apt.serviceInstalledRunning "ntp" + & Docker.docked hosts "ancient-kitenet" + & Docker.garbageCollected `period` (Weekly (Just 1)) + + -- For https port 443, shellinabox with ssh login to + -- kitenet.net + & alias "shell.kitenet.net" + & JoeySites.kiteShellBox + -- Nothing is using http port 80, so listen on + -- that port for ssh, for traveling on bad networks that + -- block 22. + & "/etc/ssh/sshd_config" `File.containsLine` "Port 80" + `onChange` Service.restarted "ssh" + + -- temp + & Docker.docked hosts "amd64-git-annex-builder" + & Docker.docked hosts "i386-git-annex-builder" + & Docker.docked hosts "android-git-annex-builder" + --' __|II| ,. ---- __|II|II|__ ( \_,/\ @@ -255,22 +257,25 @@ hosts = -- (o) ` -- temp for an acquantance ] ++ monsters +type Motd = [String] + -- This is my standard system setup. -standardSystem :: HostName -> DebianSuite -> Architecture -> Host -standardSystem hn suite arch = host hn +standardSystem :: HostName -> DebianSuite -> Architecture -> Motd -> Host +standardSystem hn suite arch motd = host hn & os (System (Debian suite) arch) + & File.hasContent "/etc/motd" ("":motd++[""]) & Apt.stdSourcesList `onChange` Apt.upgrade & Apt.cacheCleaned & Apt.installed ["etckeeper"] & Apt.installed ["ssh"] & GitHome.installedFor "root" - & User.hasSomePassword "root" + & User.hasSomePassword "root" (Context hn) -- Harden the system, but only once root's authorized_keys -- is safely in place. & check (Ssh.hasAuthorizedKeys "root") (Ssh.passwordAuthentication False) & User.accountFor "joey" - & User.hasSomePassword "joey" + & User.hasSomePassword "joey" (Context hn) & Sudo.enabledFor "joey" & GitHome.installedFor "joey" & Apt.installed ["vim", "screen", "less"] @@ -309,14 +314,14 @@ branchableSecondary :: RevertableProperty branchableSecondary = Dns.secondaryFor ["branchable.com"] hosts "branchable.com" -- Currently using diatom (ns2) as primary with secondaries --- clam (ns9) and gandi. +-- elephant (ns3) and gandi. -- kite handles all mail. myDnsPrimary :: Domain -> [(BindDomain, Record)] -> RevertableProperty myDnsPrimary domain extras = Dns.primary hosts domain (Dns.mkSOA "ns2.kitenet.net" 100) $ [ (RootDomain, NS $ AbsDomain "ns2.kitenet.net") + , (RootDomain, NS $ AbsDomain "ns3.kitenet.net") , (RootDomain, NS $ AbsDomain "ns6.gandi.net") - , (RootDomain, NS $ AbsDomain "ns9.kitenet.net") , (RootDomain, MX 0 $ AbsDomain "kitenet.net") , (RootDomain, TXT "v=spf1 a ?all") ] ++ extras @@ -350,7 +355,6 @@ monsters = -- but do want to track their public keys etc. & ipv4 "80.68.85.49" & ipv6 "2001:41c8:125:49::10" & alias "kitenet.net" - & alias "kite.kitenet.net" & alias "ns1.kitenet.net" & alias "ftp.kitenet.net" & alias "mail.kitenet.net" @@ -377,7 +381,7 @@ monsters = -- but do want to track their public keys etc. - some static websites - bitlbee - prosody - - (used by anna and daddy's git-annex) + - (used by daddy's git-annex) - named - (branchable is still pushing to here - (thinking it's ns2.branchable.com), but it's no diff --git a/config-simple.hs b/config-simple.hs index 94562ee..35eae40 100644 --- a/config-simple.hs +++ b/config-simple.hs @@ -29,7 +29,7 @@ hosts = & Apt.unattendedUpgrades & Apt.installed ["etckeeper"] & Apt.installed ["ssh"] - & User.hasSomePassword "root" + & User.hasSomePassword "root" (Context "mybox.example.com") & Network.ipv6to4 & File.dirExists "/var/www" & Docker.docked hosts "webserver" diff --git a/debian/changelog b/debian/changelog index b3f4572..5492366 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,39 @@ +propellor (0.8.1) unstable; urgency=medium + + * Run apt-get update in initial bootstrap. + * --list-fields now includes a table of fields that are not currently set, + but would be used if they got set. + * Remove .gitignore from cabal file list, to avoid build failure on Debian. + Closes: #754334 + + -- Joey Hess Wed, 09 Jul 2014 22:11:31 -0400 + +propellor (0.8.0) unstable; urgency=medium + + * Completely reworked privdata storage. There is now a single file, + and each host is sent only the privdata that its Properties actually use. + + To transition existing privdata, run propellor against a host and + watch out for the red failure messages, and run the suggested commands + to store the privdata using the new storage scheme. You may find + it useful to run the old version of propellor to extract data from the old + privdata files during this migration. + + Several properties that use privdata now require a context to be + specified. If in doubt, you can use anyContext, or + Context "hostname.example.com" + + * Add --edit to edit a privdata value in $EDITOR. + * Add --list-fields to list all currently set privdata fields, along with + the hosts that use them. + * Fix randomHostKeys property to run openssh-server's postinst in a + non-failing way. + * Hostname.sane now cleans up the 127.0.0.1 localhost line in /etc/hosts, + to avoid eg, apache complaining "Could not reliably determine the + server's fully qualified domain name". + + -- Joey Hess Sun, 06 Jul 2014 18:28:08 -0400 + propellor (0.7.0) unstable; urgency=medium * combineProperties no longer stops when a property fails; now it continues diff --git a/doc/news/version_0.5.0.mdwn b/doc/news/version_0.5.0.mdwn deleted file mode 100644 index 2d3e147..0000000 --- a/doc/news/version_0.5.0.mdwn +++ /dev/null @@ -1,8 +0,0 @@ -propellor 0.5.0 released with [[!toggle text="these changes"]] -[[!toggleable text=""" - * Removed root domain records from SOA. Instead, use RootDomain - when calling Dns.primary. - * Dns primary and secondary properties are now revertable. - * When unattendedUpgrades is enabled on an Unstable or Testing system, - configure it to allow the upgrades. - * New website, https://propellor.branchable.com/"""]] \ No newline at end of file diff --git a/doc/news/version_0.5.1.mdwn b/doc/news/version_0.5.1.mdwn deleted file mode 100644 index c523ae6..0000000 --- a/doc/news/version_0.5.1.mdwn +++ /dev/null @@ -1,8 +0,0 @@ -propellor 0.5.1 released with [[!toggle text="these changes"]] -[[!toggleable text=""" - * Primary DNS servers now have allow-transfer automatically populated - with the IP addresses of secondary dns servers. So, it's important - that all secondary DNS servers have an ipv4 (and/or ipv6) property - configured. - * Deal with old ssh connection caching sockets. - * Add missing build deps and deps. Closes: #[745459](http://bugs.debian.org/745459)"""]] \ No newline at end of file diff --git a/doc/news/version_0.7.0.mdwn b/doc/news/version_0.7.0.mdwn new file mode 100644 index 0000000..6ce0b51 --- /dev/null +++ b/doc/news/version_0.7.0.mdwn @@ -0,0 +1,9 @@ +propellor 0.7.0 released with [[!toggle text="these changes"]] +[[!toggleable text=""" + * combineProperties no longer stops when a property fails; now it continues + trying to satisfy all properties on the list before propigating the + failure. + * Attr is renamed to Info. + * Renamed wrapper to propellor to make cabal installation of propellor work. + * When git gpg signature of a fetched git branch cannot be verified, + propellor will now continue running, but without merging in that branch."""]] \ No newline at end of file diff --git a/doc/news/version_0.8.0.mdwn b/doc/news/version_0.8.0.mdwn new file mode 100644 index 0000000..69dbb92 --- /dev/null +++ b/doc/news/version_0.8.0.mdwn @@ -0,0 +1,20 @@ +propellor 0.8.0 released with [[!toggle text="these changes"]] +[[!toggleable text=""" + * Completely reworked privdata storage. There is now a single file, + and each host is sent only the privdata that its Properties actually use. + To transition existing privdata, run propellor against a host and + watch out for the red failure messages, and run the suggested commands + to store the privdata using the new storage scheme. You may find + it useful to run the old version of propellor to extract data from the old + privdata files during this migration. + Several properties that use privdata now require a context to be + specified. If in doubt, you can use anyContext, or + Context "hostname.example.com" + * Add --edit to edit a privdata value in $EDITOR. + * Add --list-fields to list all currently set privdata fields, along with + the hosts that use them. + * Fix randomHostKeys property to run openssh-server's postinst in a + non-failing way. + * Hostname.sane now cleans up the 127.0.0.1 localhost line in /etc/hosts, + to avoid eg, apache complaining "Could not reliably determine the + server's fully qualified domain name"."""]] \ No newline at end of file diff --git a/doc/security.mdwn b/doc/security.mdwn index 5576bf0..075d68e 100644 --- a/doc/security.mdwn +++ b/doc/security.mdwn @@ -27,10 +27,11 @@ Since the propoellor git repository is public, you can't store in cleartext private data such as passwords, ssh private keys, etc. Instead, `propellor --spin $host` looks for a -`~/.propellor/privdata/$host.gpg` file and if found decrypts it and sends -it to the remote host using ssh. This lets a remote host know its own -private data, without seeing all the rest. +`~/.propellor/privdata/privdata.gpg` file and if found decrypts it, +extracts the private that that the $host needs, and sends it to to the +$host using ssh. This lets a host know its own private data, without +seeing all the rest. -To securely store private data, use: `propellor --set $host $field` -The field name will be something like 'Password "root"'; see PrivData.hs -for available fields. +To securely store private data, use: `propellor --set $field $context` +Propellor will tell you the details when you use a Property that needs +PrivData. diff --git a/doc/todo/better_privdata.mdwn b/doc/todo/better_privdata.mdwn new file mode 100644 index 0000000..1ee9e14 --- /dev/null +++ b/doc/todo/better_privdata.mdwn @@ -0,0 +1,12 @@ +It can be annoying to need to move privdata values around when moving +services between hosts, which is otherwise often just a cut-n-paste +of a line in config.hs. + +It would be better if privdata were all stored in one Map, and the set of +privdata that a host's current properties need were sent to it +automatically, rather than the current 1-file-per-host separation. + +For this to work though, each property that uses privdata would need to add +to the host's Info the privdata field it uses. + +> [[done]]! --[[Joey]] diff --git a/doc/todo/info_propigation_out_of_nested_properties.mdwn b/doc/todo/info_propigation_out_of_nested_properties.mdwn new file mode 100644 index 0000000..9e69b0b --- /dev/null +++ b/doc/todo/info_propigation_out_of_nested_properties.mdwn @@ -0,0 +1,28 @@ +Currently, Info about a Host's Properties is manually gathered and +propigated. propertyList combines the Info of the Properties in the list. +Docker.docked extracts relevant Info from the Properties of the container +(but not al of it, intentionally!). + +This works, but it's error-prone. Consider this example: + + withOS desc $ \o -> case o of + (Just (System (Debian Unstable) _)) -> ensureProperty foo + _ -> ensureProperty bar + +Here, the Info of `foo` is not propigated out. Nor is `bar`'s Info. +Of course, only one of them will be run, and only its info should be propigated +out.. + +One approach might be to make the Propellor monad be able to be run in two +modes. In one mode, it actually perform IO, etc. In the other mode, all +liftIO is a no-op, but all Info encountered is accumulated using a Reader +monad. This might need two separate monad definitions. + +That is surely doable, but the withOS example above shows a problem with it -- +the OS is itself part of a Host's info, so won't be known until all its +properties have been examined for info! + +Perhaps that can be finessed. We don't really need to propigate out OS info. +Just DNS and PrivDataField Info. So info could be collected in 2 passes, +first as it's done now by static propertyInfo values. Then by running +the Properties in the Reader monad. diff --git a/privdata/clam.kitenet.net.gpg b/privdata/clam.kitenet.net.gpg deleted file mode 100644 index 4b85bfa..0000000 --- a/privdata/clam.kitenet.net.gpg +++ /dev/null @@ -1,82 +0,0 @@ ------BEGIN PGP MESSAGE----- -Version: GnuPG v1 - -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 -=5rkr ------END PGP MESSAGE----- diff --git a/privdata/darkstar.kitenet.net.gpg b/privdata/darkstar.kitenet.net.gpg deleted file mode 100644 index 9a6de1c..0000000 --- a/privdata/darkstar.kitenet.net.gpg +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN PGP MESSAGE----- -Version: GnuPG v1 - -hQIMA7ODiaEXBlRZAQ/9HdpfvTbfOnyqLlEK1WC9QO3HrF9w9yrEH8hCrVFJ/86r -xHK62+7I6wrV2W1UAHRx1b4H9qEkbD8+MAmjB2JYVmJUqvdzNv1jhsWwPpAcTQN1 -RVWR95Auc2rjXXSiZRudLaWdxZdDBg5PWApH5+NW5grtNRKsTbYB1/No2iYJvDuv -WcbBkuFyEa0WbRiqUaUIyO9XAGyj4hqVDQSXH2Gzei8oB3PZh9+Lwv7i05lvSup+ -dtbtEsEdDiJbCTzIakV6vEQT1BDVMpe6jRQbv7c+LXLeM65Tpl+2hnTPSTy1zcr0 -bjfkFa6A75sHmIf0WGKAZj+jmNchp4AMdjmoMiXkHacDsBw623NgiMgzUnfWVkFm -BIrdk5AGBi50nqPxwtY7nWd0cbApvNvT1zlx8MlRBSZQ2zcijo5AjiCwb+eLLVhv -6oiKqpYGC1XpdNFFsaKHnHBCgsPIIetwx4ng0+lvRgBO+DEQ4RvvdKMhy/3nXrpz -NVdr/gG+HMBW1BjyCd9ArmTtSITQWDT8vnLmyFbc0aJ88c2rEjv2BpXmhKjxEoEn -IMxc3/9cLrVVRocnlq7YvKDZpfuwjgDs86D3e03Up7hQZhLU4+r8Wq7azxk3wE06 -lAQIS0OwCe75EZvVWYHwhZ3vEoBE/TeqeaRyhKpofFS5GvtIJsZBjenmRcdOJTPS -wDQB/c3XkjuIrJErMBx/KrNQc2mAjcUpvW4+Ukj5vtpusi3qmSfsyaVJ4ZS9SwVv -7RPqLsH5Iz3Ga6u4of/mg+iG/wqJPJy2A9A/XOnsNVCVR3a+NxjPqevEjW1Pr6RL -SOMQSK6OuwuT1H13M1Z7R6dbg+pCcbc+hek9/6KzeZS9q4Di7aqq7+XeDr4c51+Q -2ojS4DG0/vAJmOO+E8ZatGiwdI8kmELrzAF8zzGz+ZujXSuiPXVd2kw/JdfUaTRq -KrtNhiGWWM44YWS43TYuYCoVgokrdVXzsZyKyhHzgXKCits3R5+QcUgUx2vESuOs -+FdM8fAd -=a0dr ------END PGP MESSAGE----- diff --git a/privdata/diatom.kitenet.net.gpg b/privdata/diatom.kitenet.net.gpg deleted file mode 100644 index 9643cf9..0000000 --- a/privdata/diatom.kitenet.net.gpg +++ /dev/null @@ -1,372 +0,0 @@ ------BEGIN PGP MESSAGE----- -Version: GnuPG v1 - -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 -7/HW/0mHEZlf846FaPssiNT+Ieyj9Q== -=lxmO ------END PGP MESSAGE----- diff --git a/privdata/elephant.kitenet.net.gpg b/privdata/elephant.kitenet.net.gpg deleted file mode 100644 index 41eda0d..0000000 --- a/privdata/elephant.kitenet.net.gpg +++ /dev/null @@ -1,111 +0,0 @@ ------BEGIN PGP MESSAGE----- -Version: GnuPG v1 - -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 -qQxw6B5ksDEcXeNKy5xa7gfOl5OJ+WxvbNHOy2s= -=oXYz ------END PGP MESSAGE----- diff --git a/privdata/orca.kitenet.net.gpg b/privdata/orca.kitenet.net.gpg deleted file mode 100644 index 51114c7..0000000 --- a/privdata/orca.kitenet.net.gpg +++ /dev/null @@ -1,57 +0,0 @@ ------BEGIN PGP MESSAGE----- -Version: GnuPG v1 - -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 -=ZBoU ------END PGP MESSAGE----- diff --git a/privdata/privdata.gpg b/privdata/privdata.gpg new file mode 100644 index 0000000..afafeee --- /dev/null +++ b/privdata/privdata.gpg @@ -0,0 +1,439 @@ +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1 + +hQIMA7ODiaEXBlRZAQ//aeJpq25yXbayk/fuqQZITiC+BVuslYxKMo0lBC5D8d5D +EkTAEvqHLUip3Ikl3nTVDMabdisxzAjwl6nDBzUwTMxPMAh89gOOwxmsfFbdioGn +dbKweCAuZ0qXZGo8viM8ZjA9kag3sOQzqnmLdVa77Fj8WnfNEsEAFXZFU6aXLlG2 +n60+bhdo5858uK1dEItdhT0I2gi2SOEIJ3ojRs5jvV94X6+imSfpgrp9hwKBrffK +Ao9nL9e15KHG39wx/ZgBMxP74HgdQsfmzdRh1eW8hqj0F4SnBzPoG9x/cTrufavC +ZViSTngiROE22iEeAFHnPgyS1fo4pD1Vm0CxpqThhVn5yaEKBliRsOtOP3wIlAXH +7YKtSbBlHx1d2jRQdT4y3S6f8BdP5feYXjOj1BO8AvK+nJEcU2YhVEuhigXgNMDa +kbOTB11DId6WkxORvcVmXM4mcSESTVerLdqH2otpa7dxv+EWIj3mpCxLIB5w558T +0gagUVobw1ee8y6FxktCj93KE75AjawvcPqub7Yoc7J4PdzbrGPweepxjtGcgi8L +WNSkMzNk5d9wnm72TopaTQam5g5RndgRL1o4yqgchHr3fdzmA5CfACxitXTUwQsV +sAEjo9nelmYin81seJalwLFUr3Uo638dvu4RjZZLmm0HQ24RIFXPBsClY9HAhAPS +7QGPa3cOY7PkqS6gGA/ypN6/XkPsvq0QasFZbnQ26F0DQnvLXQroPFxeOgz9CG1A +WHXdet0KkYB5e+MRcnDTDjOwfPlaxCzSsduwNR4tMqVuZ78gDbMALUH+utYL8pUY +FojAnt5nigbwcEN0vaUY8WKTrOCUv4nQiu6jT/6YFDWRDHPVqytSA6rfWew0iO7j +K2VbqSvOq3EkD6Yi4N2CNH1cZKo13eQ17gQ1sIntOip1YYbUDYLml0vpfXMVRGb2 +1ed7SR2xmrMk+s5bUtoNtX/IQLB/fcsuCzRmBQY+PJKkRlj8lW3t8xTNheGoTu6g +EmK/LIf97PrIO/PzEDAjrHGD46V6L6qukH97k8HDkHNl7aox1ANYj5i/Y/DR/Jdp +qP3rtlmiHgpLX8i375JxJqGkIQ+ZzH1v86HrxVKFsPS8A7lILVkFkvI//H/TrcoV +KaF/LdOqQsd0OuKd0Gs3gxLytjaKHxK/AMHz51/VAgOZ11SfMvQpZRdt1D6ygAzl +PDdb6Nugu/Jt6GIe2hDmWHhxbWvW85sGxJft8tpZPvRNPolFnf5VjYluQSQq1JAl +QAX5RE3yY6ngW6KRmSbfWRFRNSXC9B1lxQ0CDgIPaz96KrzAH9M83Vv5RPQSICn7 +8F+t4YOrZrg4gD5qMT8sHIrBkvognvoT60ILz8qTR1Ou2w4xPlQUVPIfC1YCOY30 +gNo4XsrsjJxMtjOsx4fr6eLCaqzSVm9/gcxMJJQqE+ukPVHlX67WrDwEufBsSxZr +Dx9ZkCoiiVE+i4UiBztmd6r68yWFMNT2d8VHw66Y1IbLNZS8ZjZqo/OQtQwbnCnv +01Rn4xpBnjasMClGOBt+gNbpc6uziTORKjTLOIxazoVqoeWekM2ELrFCvpGS6YOz +rZr2psYRP6TO3AG4+MsqkK72+YcMMqtAnq6IM9nv3wEddAz7MQn9VLc+xGYbiXdX +yfEQxx7Gc0J6TJnIzASpRiwbpPlGvE4DtuFpigFzjjnyms/usj2d8HrZLhiN9xnS +q9O4ODIvEECGm2H6/DRjCbLPbKpEMqFu4OLW+2lUW9IK6YPBRJLjhepLHzEHZDrD +FPGvj3X3psMsT/nc6v3Rj+r5GqQeTySEBPkfgon5gs2kaBFbmAlzdOEimZJXKnC3 +zE6c6WdpiNs9tCNvxzkEu4TGk0KOE7NvVEgEKETMjTMYmccoueZFRIM0cdzg8s6Q +Sx1KeSHjJ7Z1L+aIFLZ6UyL7pFejjroh+1MzMf+ml7hLt/mrOcg2f+zm+cgJUQzy +hoMxXKNGc8w9aIsjSWcPhsuLJZLSIG/AQdd7fY7xJDIJ1fd2osX67ixXEiydz8BN +DiDlMVkNKzQp+r74RmNmJpziMBMtzIfHuk4YKmqS9EKiAMn+3APsFr/adyP+RBE6 +qBnvnDclbqviLkiXg2b6eQ0NZDN4NTEq8I6BSFKXFxoSJzeG/GkF8SvHUQaAHkCq +EmUxfMYw8cbQlYeRl/gtCiWz1WH2LBRulZvHVfiUzqjFY0GhnJzpLo/Pwjj2EzYz ++kONv2jdLmSa7Ub80XzngODjkun0xwIWCjMHa2F7GioZwyYSmxP2UiCCGAnOzDsZ +J/x7Y7/QQbDAaBQwpLPu4Ozx/7rEAxoBKTqjUWJ/VaQ+jrOhWuWCHWc3WIOo7YXz +HUK2kkw/7z7+l/I8w/RkX9uAxRIdD4c2SOAiDjK0EHTk0+e2bSGr4AMLfbtOQMOz +hiLLeMLXfT6uH/Dcp69URnfwH2h2CsRRK7w91/0gx4W8maxKbJXOXX8G+q6mGHZa +G6CPprGunt1Ycnb+CD0pmuPl8+4/jlkrz5vp0h4SBHEf47OgqwEuhqIvBeFJBQR3 +nccoOyNPkmFi131Drl9W+KSBZB6saXgOBqSDs/68zU843N9OyFLcGZANmwvTuA0S +LXom64j94cYrt4kq8uI2RYbNqxAGie7q593cbdnlj9nY4/tNolrphTiC/eFi7Ksr +113g2B2kvoH5lAupBr/LVUTMiit0wEWLcxTCN7rfYelcuBakRvkJwMu2UhG40EGQ +tMrWeBpJr4MeupSqFOvvVZwzIU/zA3X39cQm94SPv0N2gemeWj53IxzvP44GBENc +t7pFgNnnvPBCn7EeURviumAaBTWppTl77mz+eOlHdY2mnhV6M/3Gbw3PCflVQ+sb +r9Xvjhk6PZ3b0fQuQI1Lh8wWlwNDwJ9KkiIW9nfBYVFguKuDAwq/1P994Al4ae9U +C9XlhdNofWO9FhYdv6EK4lHFtsu8kx/Q1ilBTqqSCpm70ua5MtSYd1+Qk1eugelO +gvD3Q2GCIxhzd/gEIJcMpapeGJrLhr5YF5w3kInI78vNbZzESPZzR5pt0xzO0odK +xtYlXOGyc6bdq9pMZXLOGfsiom7IY+TZd1vRMKSbjuM2cFODSmwJus0/R0fSqL2r +2qhrL5bxR7v2mV1DM7mBFdFKSOVenrnX2QOgmZu41CoDyBXi+Y0Nosemjk9PTqs6 +ZQFu6malOyvWpOcnXNc51gcHDtQAgJ3efky5h/mXDem+8lWKT2zGT9sFN3p+6/Sy +oMJ2/Uu+XyaxkHOE8PW5qYFLopwkt8vmPBTmfT7368wVCTmIbmyxW164GbL3N0oE +aqM2/QnVLYXzhv7WXaPNuUDxWNeXhyoN9zZcDaeBYyctbLm8dFTEEbeZvFF5g4+U +ItTEZd0+b9DycF4C5Yw3wieuKFtzX3DikJ07i4TU0HwkFIBXZDDGMtsHB2DUvpeK +Ijk0tIVDyBfambdm6Is7OKtpmuHhPhtHZVPA/GDK7n1t1csmDV+HnXF6w6oRfsbK +aAptfc4mkeGlijFxOCS8DWLxKMXF3PhWHPQZI3x91hhujUDe4X8kAvOA+k+/iwbL +ZNXTXaoxbVLUaKLm8Y9PKNW63WzVsC8zgP262N/o+eJ+Fq+HJnTzsWAUDHvrV2XR +hcKUVxM9EivLNwURHeu1GrWJ3M/775Qyo+AB1eJ9IjPPLxM0h7LJV8KYyku+GMWn +P1UnlMlbqojj+qPezn2MGZggP0Nh8dD9HbFbFNzZI0MF8OWNAsvHD1F0IwJ89E11 +Kkx4uiQW0TEY7oghIUpRqKqsMIdAhfFwHTzkqDmXcu1JxMayAteAclTmWiuLapje +Z86RLF+d6HPr9qIluOJPONYsA8aFp9S9xqJ/PWcn3BRXxNJWv2PREo/TnGVo765r +hHfiIj7DL8kE3TR+CWg9Y3zjhpScXaqmzMbU6EhS01EIdFLNibEh5sKyqdzhMzxu +8dWAASbfzWDwugIoTIPPdBixIAfMQYFjUoi6SBaZ1peRVvBop3tN78WD5Ql5Qw4n +LY00EKpXuMPdS/2XrFXmRMxWLqRyIKYaGgNP0dGLMuvlUgHcl4GWGjIkEYw7jQen +/bs6GYTmrciXLPEWarZtv0qYMHENWvWMYWeTgstSbvSx8/uqn6tjQhywOkMoAOlf +WxVMqZwx94nDC/+6RPiTXfiD9vJ9SduLvAk989GWndkJJr6ZGunNtQKap+EBMMpW +HoLW9v+Yp7emm5uJPgvho6a/aTFeXQ7UsEq1uBGRxrRwaOyxlRxyiLF87upbiN4D +Ngs+RD792PsCiex45taJPeFchDmFQLjlj8+mY8sHOh2OlsfpIl5qq0CtU2Jr4RN6 +saXnQi8pKPW1V1sOvYcp0/vTx2B7eiIJP0JHxm4SgACU8S7UE/Avqkb09h9EGIlq +EPaq+wWOk8MnLW4YYNPDUNdTnb8iyduHVe2o6HwI67rNsstKpeaKudiFM6vFLkWh +3uiRoi1ZFYH+Y3vd34aSHV3K65h/N+Sv+X+6DP03+vZ6zg84mveHaOyI3VLTpadO +QicabiXcJpsU6trG+C/5Q4Z78O0Y+ZsLAFYP3Ro6KTpnV2XZKebHNQMfMJ3bClmV +2YIxDP6Iwss/PEUaG5R6Cg0YWcqzU0BuEeZelSpvOsfqh97T+yf73v108nz7CbbE +PIXCnf9uYBF2lDhO312NjfjkkpV88QX3OHqOMuIX4ULK4xIXMTm+ZoOI0RjS/VhC +5tbaE7rTH6L7vGG8ZxVSNA/u4QeqHTACJ18DhiPWWVnbeSWe19t2DWSU3+vFJiec +TsegBPogtW7KRcWul3eqwPvVRd0WtE0EHeh/vWKVE90VmPw7SfTkSAW7tl7//yt7 +83oEpj6Ze3mxQ6nXFDX8zwugClkIP6E8cC63x04ObKUaSIrAYBfgSlT2UxPg9MJk +lbNvDAjtar26ZmP/6ifYqhHQydiZoypN1ccMPjbtKTrmjY9/ZfKpB75KLRj25ZfG +2PAkcLHPRHDuhl0T4S+w+Wye3ollrO3OA7pI3TvEk22fS1GNUjmphUhGq9/EFVdF +D6T4e+feVI3sqepmOge1GgCaBrUEpt3w51p20GDLv9C/DfmIrOlZILAq+xXH1aNB +7QZTMzJ3RmPWvPrZTusic3ah1jO9vP2+WkB9DxysLp+DW9UVmW8LkVJsLoL0VuuU +CwBNoSKmIiWhTr2b+XUr/GnIzsssjQkalGDXtobDYz/LvQmMjHc2QNH0dL6GRvKe +33in7A7VbswE76Pbuof/OtICHuPI3fg+SlQK2vKLTFJOyLKKKvUqK1ZtLGX86IDq +f71oJT4MApic6Ep+Cd8aGsz5hFdy5KfRZX0rJ4+lwsCew3USh1GyOK7/a2EoPw7E +iTVmAQ2KVWjF6dADDRbmLsoTT4eWLb8TOlSnF6LCAe4eCwcmgRLKJSJYhkpp7JVB +e3g9qRkLzv6ypRJD7GTwubVyJJL7vW/c1v8PokTHYGfn/cM4r2iZTrxDiMvk1+0n +1HKA0jhco5gjTSrah8gBFD496VCklhLr8kqcxK3TGZgim0KstF2drrnKtT5xfL6+ +8EHY7e+Z1AdqYj8bINOKDs2x1rWNMZEkhxn8GUDodh2rE7Gfide9PARrZclwupo8 +0I1MmzxXCuoXSGMAoDGRRCykjEbeFmIvprBO9WiP9SC0ruhg2nEElxlLuri4Uen8 +nV3Age5IVcXLHl1Tsruc459glWmXY52k3WaJk4Up+pk7KpsTVEO47O11tYwtFBR3 +B4HRAOTWVEA89iXYTVlYFJzzrB2KEFJ1qEZHdLxHhOzLKjaXKFNwJikW3oAvVpPp +vOX3dgNPfnUA0vZoczEQW/A4oSPM21vTqcOs0tceHQ1pjE5ZL0iO4TMHTwbeY6cl +iq5SR3rC4O9N/uTYX3DWPU1JY7ovqMqjokDNkMOFHoN5AUsCMxRXj5IeQOP7jxyX +CTIbr59HfaC9+Y7w9VpNDW74SNxjccbTA9tUgfBX8DtCGdF5sDWpBo3JOD1pmZIu +EB1aoZbRnwKvv/6I7mFfeBV0CojwbbEaYhgQYX+l13KIuyonIFybEWUUVK45LdtV +pLoOyh3VgzknPWNqE8NTyfw40DIlkIA8i6wvRpAw8kmnx9Pdg2XuMEJYYb+CP2Sj +nR7asXXPsyWdm+qxgJqSCxU7RupFD72sereADF1rIgwU+UFiLCEIoi06bzAS0QDe +cijeTI4v6ZLr6lTjeXqoNcJlYaQZvH5Sh3RWLcR+XeYXZsstRbpVDITMb9/fAKMv +ohu5DeGEJnx0Da6wdOW8Cz8xtxe+yuwxkqyd2lVB77bkzYjfWW26LAKhVVKiEyR3 +QqJPF53TeXjh4RDxrQF5iYJkBJ98jPAUxGaTzupDTxIxVMRgKrNbK/rYmFCyeE5o +DAawqg1/NPISUMjgHNijCMg+dVPHDgP9bRdDrUPCimQ8mfmum+1gS+kmyhxCj4op +0TEB/XnC6QlTrK/kCCxv8q2gSBdR9bA95LkWBtHkI8GTDhnEWl4R2uL8DR3y13wt +dPe8yQgQIY41UZEAQzBu3Y1hBxhLlRRcPtz4/C4BH/8JqVasZe/XA3J9As2dmSZD +7lGFerfkHVMAoworZa8qf941JxWue7HP7kweos5KsEbtyHhF86Rq2l6DiUT5BWFR +Pwge6+rK39jSnqSGHRSASrE7/M9ZTD2f7tPHqUnrLQcxFfjWE3DOo8muwZOYOFWG +ZpzRli3k9pvtQpfyrexJ254OuKgvViRWmOQgrbJEsQ7VisbwGLalLUCr7Oi3Y/gV +EV2H9NcJkhXr7S1ObXSPc75j5q0wgeV57bSgsMIbNqSaVeIqL2ncWH4Ga+VFir22 +BvHXUcMXT+FyKq+tUHK/Ot/qFmS0D5Y/QvXg1xDSg7zEePA8NlC6sibYxFklh6qE +PgVnJ0PTm9dv3hDcO1NpRPGHi9fbFJ1oy4q7XiYOmux2hDM3+dkJGkGS4+Ir0vPj ++WrQ1UTaKf5GCSL5pUP/zc0BsXlxvyrQ8Eczs/Nk5BZx7UmT8NlK0RsFqpGV3CG6 +VyEjVJ4D5avDV0P4zB4xIisLKvGbM1GcQp8WKbMjzuOhueI3UzpcpgJPzgjgdayE +bz0Qk2FgkwB0oxHmN4fEzcIFLk4t3OzVGcP4uSjEJsmH33jAS/1FhNd/8FJr0dtN +CWkPhZmc0PG4GtLcBvgag4nvwY+9D3Hgl0o8FpV/nesWAl0ySVxhJMM9Y/37TQyl +fLDgmpQjYLshUfmWWGYwCqHa/Ov5PBITuPOZWK5kE0bIKLTl64l7cd9A8lMYrMe/ +23bCPxZMe5ZIInB1EP5jfuiAVFbSAu/rPlSQVlK7iXtrYTC0pSNsuVxzpU5unsG0 ++tqKE1NDBTJOVvmiw11bmC11HIdl+75gJ+hWnlHs78o5gJFuMpJsv7koDustEkJx +I/+DB0i9s8De2QGuy1dpZo7IpitvckaAQIFQ45lidaAaZv3CT5wxEhB5Sl0kbrR5 +tguQ/y50S/RRdklx2AtocJfnJVmR0Pc9CxxYPreHx8fC3uVV6d026GFnUXRPlWKW +ftiyL/lwGb0iEt3Y5K0CUucDjSi0iCNEjx1J9QBXc8hg/WjFih6egnvrvZWx8J5B +/sc4VukW4kHjs6ulJ+cg/by64uTc6jmsAR9vzVC7mIJPfWp0f7VABrPyTEmcFBJd +5eoyPUyGIu8C5rnOBOmcjYM/NNEtx4EqukVLha5YZwbVGYa+rC+xN07NNLQbFvWG +RZVSyt3pVioLfjQ8faHHRIu7qolrICvLP7LaV5jyPyrKcwpb46IIYHOk+/iJ6MOF +NsUjPJvfraYoeRVqpm1itIXCcM1g7uyTmc6Fg264+LVAYIlvLwCWlOyM1QeoqBVG +PL+k1xY50jwg5hkejFRDaE8I9+0Pa/bNF/XknDcZRv+sHjVpTU/DMEIN1jK9TM2G +SsltNHQTzYgKr3kYL0ud5lTRgSMU4tA0UuVOqn6OuObXeswsonfU2fqvXXrqE+W+ +Wofr3xJMphG3c4cnMmyDjUVOsl1AyL21ZB0R8kCqBDJLG7yyVTHjf/avpeTUfZwx +HBIpiZKkhdVkFo/du9wO0CfvHB89l8VnK0+js2OXZzDwPB8KCWTO8VllHAosqULI +Cp/p7F++Eyq9PKg6vOw43ZDbpKBoacHUgLNFd5OJMScQdKSGDCZtT7NRXzxGsoFa +KkxMCc8VMN3hwjP00EP9cHVfiqQ4d65lDE11yK3IQsBOKaF3q59doHxWOBH3NMPB +g3wJ+dEFDcMwwhhkbIjNPkROHeUY0C/6L7m3DKpV/44seVk6EhsGGNpsu/L+nDvc +pZwRmpWnioB2QTDbs21vTfr8iI1rh0bO2Kc5lfFXEvZYVxMizrjdPNCSty3aX+oy +e5oeDUOXafHSZhtbgrHEWXSzZv/7/endB7/ZYkSx7/5jBFsVZcMdWHnP4dkIBtLz +gTimwG5XHWiIMs1Jsunu+BUQfzTzTGv94o5E688XtsZRPYasb2JRdrDaGFdW8P3Q +w1bxyUMpqh3GcsD/d88I2HQFZ1Pt/4IBF0ooh8WZFurZtX59tF8eaYLeBBEkIKX+ +N9KHxNZHcXV8sCez8HUFKzTm18m2V/PeHxV6zQ7wq20MEHbq3EjrdVme9/iJbHjL +KBmqztjnkb+ux1QTV1871W7B+Xx06bnvBPPI5R3GvWbN7nY227C7FtMbQg6Lrgxu +FX5ARg/QhSgvTzJW4sOkZHYx8qDFbzovjWJxtsEGwsUnlfvAnL3FoK/3lpH5AXNW +mFjprgSYMZFRGuPEbUHd7kROwZU73Lazzu9vOpVvGaBYxz1U9JhK1NYyvo9VBP1x +euTv4yMGek7aWyICeFOZZqHa4OVymwN/HzHEzxaRBQ67garIbTzIGi2eDLkh2qFN +TzDIEAV0k5SyiBNCmFFYi2/qXhNRFOkz8BrlUGoB5/s/nn/aWT8Ll84TdxzJLVfx +atL/28krtafQIb8a6VniGzy5ij/wL+nKLzkNaGxvwOLVD+CurdnvJVOkeGatmhfN +AxDm5j7Hxrd9rZfblkyBbo+tarLYaCwc0AqKAEfKiJse8tOEPokJfJXeICwmCZJ/ +ttoaEEKkRpJSGSQxwezAz8QpkmCxW3wfBapJKOtNJLR6iIKxqa72lzIM9utV5tUi +n4sQh9hWMgb0pc8mx+MyEVjScN3f47IQqh9BSI7xadl9ZnKXPPc+SFqjqZAHbum6 +gWYLv54sIKu8g7sAPbXfxVpWK8FR7d/ODWkwK4L7v3nMFre6r1Y/QnX//ZAvX12L +LFj5FI9iS6RCeU2pPetyD/crxWOgzsJG4UGuC+6nQVBN35uGgHOtMQa3NedWVB4B +zTd88JJIylpAl5al4cIQ8HXZvMgOparfYAUR7E22jkg8+ikLTwmO7dnmUI2farQL +THksb4pB9P5XVAWkq6xhmXFOlrK/jm1jnmfxrLf6Hs8Ef8GTDXBcNLrMnrCZLMvf +hrL4Ou6zPz91dPmSy9fPExUVKut22JIefS4DGeUgEVWVGUvxb5hp/ZvQ028/VQA6 +D+ylOL11ZaCW6elpJkcTe5KtJkzM0+Q41QycYLdLOkxW6um/60wcMSiOoDOC0X59 +31fQsvAPLFiCGemnN7euge3cwsZjgBK4CL8wmQ7s/GwzB1WkJ7iPdA1qIUqRXuYa +0sB66fGx/12bbW7l+Waj/9GSe7Cx7PL7Re1EZOhy+n39PWmKB9KeFmlmKwd9U5g7 +cK1V62wuPn9M+0bbXpc5jzHI9C85PyTJ7iRb4hEFZG+pd4ITEG57EFeEd2/+HoJr +vW2OBFEnBSxfU89THqvIZPVhpsS/eo/7liq8P6MVlkEC36vtiUgC6mS36LaRGSMK +LB3EP6/TAXz39Q0qLll2dT9Tytn1OndC9MdOst/svD7QcobH4UdzdjITiPhXEmYK +1M3augOGh9NOO/rd5i1QATHOMr8meazQdN592tTdmP0u359PoM/AYXnZjzSz36oZ ++uz4GeYwW33JAelM/Eva+stf8D2t9jrdACA3PG/bammASMQgK5tihrxnRC9s4MoO +KyOCW2V/sZWIR6x/3lId8r7g2SwfUf6QRKIkcz6CsqR6TFGhOfsAOz9vIHVr+Q3Q +8V/ZmoVoyKgGlbsXq3tc8cThYnDl/WdcUT2DFIZxyOJsxIt00mo3+qnHN4lQb3Az +DLaPxefTAENXQK4lce5VdkwIPCOF5TXghpS/BwP+fY0ECPvhvLZ0UkHmdMr6frqx +1IzKF2Dhs/un0sZYHTFtQaH9/orcDe/8EfyzpAFXN1tBf1G8is2tX0rW0W28LA2O +EUNPc+T6dtPVFSWuj0nBkEUxBwtU6/1o9RmkRqx5JFnJFergALwShQIyLhs8KAaI +BQndkiGpZfsM454/boaBe3mgUXBHk62CeHb6DtptV7tim889kpAocv+kLqYSAUr4 +igUX/07Dkqa/TdTFiEzsiGAeBY5UeLN5KLuxkSj71cS7F9rQhbDHMc67YuIA2Jft +NjQBZB6RwEEOfhgpenHlLBQNMaBpAb9XGZbefwBJc3c3kZmTR8idk6YRYgZOHYcc +h6ocLXs6AXzu4D55uzpa8gT0nwop4SrcTQCINo0N1TsEH4Q8AZh/uBqJirCB/tNk +eXbUsKUloK/McpWvQJdDtqRNFhYVwALANk9RQa3SQXf+AcbMbRKD2bZ17Lzn914T +sbEiVnUFLDPA1DcJc+yfr9jrY8AOqriXpguob48XfODmhsb6+fzpRpAaY9kOuu04 +WWddWWcYIgLmD0gQPRk32NmTHhQQ3ngrULFS3Uklhbg9qi2C2lBgJwlH42W7lVen +zBqabgdivz/ojc24qDMV91P+MEk7VeSs3qFhz0jDcQNNtPbq1VnLp/dHDVqC3x+L +faUVrZ/jwpeQEJx96VtHU9+4mx5OMEtb6uosFwCBa0EsdtimF3twpTW3WedTiDHZ +S0ZfcnPIqqZ6gHMAwU7If8JdvlMq79G4rxYpFKd0tSbyKQm8zJO3fIn2oRS62Wop +5eUyigVB5XltTK1jTzJcOvDc4skh7GxDQUtDcpBbKxpze+hjjt3loNKIRnn9Kd0k +/BgenFgID9ujB0Ym4h5cjeZoSHanE3PBLFBO88IH6PBjEErKkCjomD4h0IMN90/0 +2g8yYwKFUfKbxeTlVYxGqdBXpjt1zbogeyAafzIs7rJlXdjARYoi7wo4GzPgHvlQ +EKA6aAWo0uNfmqhQOyT+yV41BDC+B5/HlxDNY9KCqHfR+wQbqqxwNbxm6oIH7Sax +DSNV0pYsroo2KIOn+SaxukY82wvj1Jp8D8hkyS7MNnpVlHgV58GbIurGUe/bsBxa +cNEM+DynNxZ6BYCZthiioSjCd/dq74WUvrh35kpnYUmprZH1a4ihbbUPauDCuo6k +jRWw3GSbycRF13J64iIr8CwmhOE8XkSklRIsTIv7QmPg/wIMlHX2DMSDV4QqVxeF +kI/1/a7Ef/Wxc2L9U+bW4jxcdyvN2pDs3B5WsACN0ari1JFoKAtSe9nJJ+LLBcrC +H5Q+jagOEc9y1wHs4Rj8Ck22muIhusOOHF6b2hDJBgnwukewUxiicKd8Z99PIGIn +yBcAJ7b64biQLFLRqZLD4t/myp578Cg/0Ic0BeQw1I3d7VPhkI8L4ge3Hz8rbegQ +bIvzB3YFUptYpubP3yB0/zWo4OrWT7FwvtTvajLcHa2HZONpRP5SpXDG2FqGVqAJ +R4W9Kk4pu7wOtFkh1viA1dkgltYZhz71CAUrpSfFf0DlPyf6BggMxcTHs2TT0Te8 +dkQaJxKkXrvEppCJQrxhamaeRliyje4IFL85ROZK28gH/Z6YIC18/JqphNCjVm1V +s+zEZIr08hyB9gVhkOWApUA4Wz7jvAeIWQiVZg4MxyYr8o7yqt6619iBiNL602kf +aFRw2DDWy1vHKTdLR+ldm3o8dHgF41k3BSVt915kaBLf7awpyiic93d/uOxyO4Qh +XLfMq3yFlbdg5vAAHhM6V93CKJIvem3njcCHH7gb46s6EyHqAhVNTL5HmmtmWx+/ +/EHRusq8Aljx4yXO0mMw+E/MGaprPPZ/Vrbajb+Sa/OKUu0zBGEX8nQTEyRCz5pB +OmlUBXSAlXDxeL10urj/ZrmRzaTfedYL4/tH0Z87JuyphuDk/7yxm5vM5cV0NP6K +WDIHkX2GNBqjGLm9FXXVwDhbJ6/rnWZjdNHqT1UyvM1u5DrKbLqwbYtGOxvJwmC+ +UsHLOewDhHIusbHjdFMh4YZ7jURR1VCGc5mr9OtbEBSwI8s2zroKiEhxwjlvzbI3 +LIh0Bq64GQ4PlMOwc8++mqlY1jnq/UR/Nf1zEx+b/fBL6PJb6M1cpG3T7sq+habl +61IQdstNigRQdiXxgxmqQ5H3T/r0HBCIabThSS2jZorjK6qyZOY/onTnxLo41NUM +V4DHMht/pqk6qszK0v2bdb0Llew23JgE0BqKgoqAAbUDrjdN/c8Z1EEp9eHIZPOH +sl7GIz23OPggXUPwcay2mRYp5PJfCt3yOmAWEjBGv3Tm/gcs0vNtCmNvFr8yuTI6 +5zAlDx+JBdKMPewTNQ4BdtRGM1aBm6dEclgRJ+Mw2WcuhqJo35YBdthf3GtAfvqT +9pp5+RN3KcUXQfsvHOoDf31BElP6sJzzQdDaQt2yEfZ2O+dFZ8iob+8lGCp+Xl5s +ROOVZy669Mn5Y7ZMsGEmqppU9+dGjrnXYwQHzTZvGuc/9QPe53L+cplfEkA1b2Eq +4mYadKChYjFkLViUfwBC53tzbbT31khTMM7nHAKyubEjOnHukzMVHW5Ta1a++OQT +3SRWjKGMW9O+nDbzH3CQMfuCakJpqzh/TBvWyowKxpCcLml+Hw14XkPHFdYJlrG/ +AU19YTmlhdysXEradLbVxgNhqK3+QM+B6FEAgHwDEFW7SyOH7tqwtDuO0ql3lLjL +DF/LDEKyzIud+Jv0Ib+XP4v3nyj3pCg4gtATOdo/XPPppVbRLjdTAFDTsjv0f72M +oNnvRAvlyDxYSXalRQ0wKK6OeL5rOtPDFj2n2McmGWx+DExC+hkdsSa9JzfrNibT +KG6V4DEjC2sjJEDXxuKsLxaWtUwXa2sb7ttDwOQ5BBR1IN8+vznLl4ygM9DMdZX8 +mEPFPDwCYWC5yPwiVnYU+OdKTlcOkjqTO2EX45pB4vgYdjJcOTteIHEEAIM1o8QX +w0IwGoBTWBRzYUw8Gcde/KVBH0A9CMV9qe9h3+9blRhSpYW/1vkoO5EMIEmn4C9T +k3fcM+e+3TIF/1eTFbYPTctGa+RrrGz30PReXQ0ukyL0zGWzplPQxeqKUpFA/P29 +5HhgFYXgPft5HlI3ZYZ2vlTLho+mCwx4p7M6WT6c88vMXx48N5OAlafzGP/ClYfb +Gv631Zf/8fkQzx80xqwB8o1E+YLyuSsS+8E55s48XZ/77aKA+IrWK9vUmtFacshU +cVZlgQtL3vHO1EwkSJSoQwEXJ0rscIW1YqKAAQuS5QhZl0CffKJb6V77oc/tyhxP +qdEa8wKvF4TKBtQHlyCiZQPt1WR50k1QmjU9wrynq+MFHZ0FedQ4wsnQHbUWNvjt +Cr+7lLaFXZpk+5trckiMVKBbpxHYcwW7mc2RI28es75GGQKuaEMJ7riIw3Nxllkx +5mvIUlUy3d6mEAsjB3SolI9jjlg1RgKkqnyX3E606XNaTrTWBr4dAnrXw+GqqAFt +2G6/G5V9ge5ovbbhCzZDJcz97mj0OiVzWR+R3WE6RERUqvMKso8AnNqLAi7YmWQK +8aRgXKkJiSTTOJDIFfi1XXilJhH9Ek7rfyvy4h+6xF3U26W0sIc12eV2HRgDqAJA +1MAxPIc8HytjLoCtiZEfBA38JkbymY/Vfm1ZP/LyZpEGtLX6Og6bcrLKTvm5C+gL +OYXKIf6eYfV6BDhjlyUqkHf/sJCpWUyfAuSVFhu8CrO3S4dx3dPuEsifSptmPjKC +HTo4Rj9SfZoLCwiX/L8VFOvanU9aFSLmZjkuymS/6WMV93UlfbxvItKTcyhmuiBq +nk5ee3P0elLSFe56cvC9D1AcM4zIwXcfmTxyIzfigXrp53IyY154R5nOe1NDjnVb +D5sm6FrfKvHIhUU0E03ez5XyFZccwc49Kj640R9JKXa2RMXav3f8xlIctC6JzCJJ +v8QECi7m5plkKszvl/7HEMfS2r02ph7NJjcb3Or/jTG402N8+23x/IpOh+HJK5ee +B39f35IIDd1chF0sHulelIIxOQi98GYffJD1r7ygONMVmhEAV2P3fV2i9pfOqrjV +QCXpq8ULShb3lgAKm5JfjSOTkwRjTIE144gvvIO7UuVjFZUYHniXCPT4X9ojnQ+p +abNxf0mqqbISPgUL9AUxfmkbnsBskIAp2l2eDqrSYxvyJ8F7UprbtdaMTXRw5PJR +j+SSZbCoIS4HB3PUZLHly4X2xvr5ZJXBp3eQqQ4uOlrN7VqH9MV3l9FCz1XTo80v +5esl1B7In0poQqI2kYPyYrC1fV9tZsb6j47CDELEOR+n3U+4ExBwlWkj54FGFDiC +SPMMyi4PwMyFlvO/Rdyk06bxS6siAbWQOS8ajQI07UZRCm4kEAr+TZqdH5ueOf/N +YCeDQhJONsEzhgT7hgsXz4HAlGrFeoKoJKsaTL6oDLaeWt4ZrUbK8bpuY8JAbh2e +VEgP3QNxnnnqukRTpCl/L6slWrM3kgu+0/6PW1OlZjFLbvHoqtE7PfRRoFE6bqd6 +CyEgC8OqWK1+XWQQnVAv8DnBonE65Eu2oqShOgtx8LEbZ4K/FebfSMCa+saA8sIS +i9IfWF4v8Ll4wy2+IPCploaCPcMfhYtu9iaIn+uO5ZDC6SJRhUiD0JTb2v6cK8zP +KO0AwQS8PB67H9wAgL48B3ssP5HRj9lGmA1gFeyTAxlVF9Dey7eqirhM8z89/LX9 +3bW5HW+MKbM8CwF83B4+tQaOjjGMfid8ckwcBvRor+pAiS3gAOfBDFgi0ykwC0bA +f8vsgPfmDPyIiOI7TtQpEpqRvaYA7veK9IxZNETs59vbt/mLcqHP0WotB5t7y9eT +/h6NFK7fXK8YwCVO0Yf59JA4gyhi0d0N89kbRg+0nmO2eCbMHVAGKZw/cm0Zosow +G8VdZP0si6usJPQ5xFoF/8Ku6hMw+VPR76F5NBNRDzaJOpFCnhYlNtJfwLwhMy/B +53nWQ3R0wx2gh7uqNaIXHI24ZQ35gluWeb+6KMrAjZ/vpVl7mEjZgL5cViEIKWXK +JK7dLRW1Kxujf+tj0+7ohpLhMn1DhzVT7nys2CttTla9rW48ix2mYjYHI26UXoTq ++CXjPe8WqfEUDoc7v99AqUMbHHW5s7AfgdHYlTj3dcZJkfariEsfdTVxrQ6QZAvo +PNvPjpewXm/PgpSYj4DeGMxx2VobogUc3rUE074p7Q4IsErFMkvBfiNBDkEBiLT1 +6WDKQnypZLCOGgCrUXbVlgxQkmJHcz9thQYlrDbBoagZf4GDrhU4Fv4lmHJKrEXc +1bolRCwEm46igHrft6C+YoP5F1Bxa+D5L3O5iy77ypumnoouznPpqFfmHyfTDD0B +GDdBs2OMcTlg5tfLP6SpGVgmTMokD20RJ+9THE8WQX404nLE7UZXZGyUKhbxlCGv +ZIZDHltDMtOu5giRQfbiR8cp3nhYAhNVCxbZWLEujpT2S9vPhcs/J8XWkc3v7X/0 +cbnli1bRniYe+BU7FAVGUz21RpkHlqBABO/jhJ3NDuINkr8c55UF+FT0AmCxL5Wr +iG1uWAhMNXX4lkKM7pQjONU2OkrEjs5JC6kyrbZeRgzlKg29vk1gR5TdQFt4Yb19 +XLVKk4m1owXRcn3S3KjFKwX3FmXNXcgE+KhBH69PDGU/b1bqoc+Md+9SxED+IdSp +MZEVfO421wl6hrIOO7KKGNze5Iqq2rOVfNwmyqWFiryuPozoLXkB7juXjV46+Rp8 +7phq3w/XX5E+YBDUErWkwFNgEsyzrL4IibML2zsSlr/H+Cx8oGvpcYKnpvydM6qU +sfENcf9ppNjA2W9RU+XN/HYNG+PajVRonXkcvs/y7DVrZiGHa3ZGXkSmVZuZ4POk +9M0njwzSQTRS8B8kzq4iExn5nzjcG7AJN2NtWY7EcpIjJr528PJqNw0Dy5X2DJH5 +qag2HTiSzVVreC+e1Zs2NeK4XJokFNe3GSxacZOpcAmzHzFGRgyx3nbI+i20/gIG +XWtkMqeT3PV1LhmvjTPcYlCWEbwzJk5khY5GWu4PratEyoYkRi0fBDFDPIewCR+U +NRfucK6xnUzSSzNmHJxWSaRD41V6LmozctsljVWQzPY978qxoVFRkirj0JG+EfrH +2iuT/RAbEwZP2a8cDME90xxtCLARqlLAfUujvaqK+hyxs6oYQzEkCsvp6evRDMfr +KlmDfWJonIacUpFKF4j/F+KaebX6TJEHY4e0OmGSTaKuEcwQoeek+PtIgghAXte0 +xpOUSnX65Zj0FDSyEKTcG7sb2/a67vmobRdrPS8PeGfHKgCVwUP7RN5K/mz61I0t +ltNv+RsnXHwboI75DdkUfbet8O3TYlvEz0qUiyBBKnmADComXwXjV9TuMjDgcjVk +dXRy9GXFwu3rxK76YKMniRp4ktr0oZ6LUd9PazD3JCaAAMCmt+w35A/G2rREGsZ7 +B6XKZW/MQi7tehS0qRDOIU/xhMt5nQkJB/ifipnrdY8yfe0M8+cALjVq+L0lRIZn +esAF2fQJ576KL9TApL6MeivhvagbTfYWiGMC6w0urLqMZQr63SEHmpAzAScr/fLS +R1zcvqnFKV0+GnbeGbgcNEzfIPrhfKFWJjmIHK4Y1NsxHUe1VWQVA4veFwwmUZg6 +RQB/WqyEUht6ea2XC7gTDOzDrHPbX04PW8F0apZFSydNmQU1bkxAwcCbKys/bqRu +rtusoQBUVRIcsAqN51J0GufqBqS9AD6Wa8vRhqhvlXasHsWcdgjG6s3/r0p7QeI3 +guBuz8t3G2esomWxOYazaGCstxcj9f/LQpyRY4VA9dbGHZ7G7C1Og/Qh3yCoEqyx +6EFwt5wtytH+5gt4L79EVePeqyczMTiW4PXS9lMpmOmzzdYwwOq0IMNKYak2KLrJ +EUqzkKT+H7o8G1sjX6+/pwok50ULsjYOPiFg03xzo8Z9NL9eIgrA5lQT5PjY6RZ1 +hwoME4Nn7J2lESntey1B/UtocUCFlEzrtSV5QhtCovDs+U6fqmO2+BTGaRMKlqOj +PqeQG9BTPWBOLym5+qASbsNtmK1e0MFdLpqW7X6by4D09PJJ9blQtRLxpfJXCm2A +7Zl7t5KHiOoUXhaX4X4rm3JcaA2v3oRW5oqQBV7q22N+mWEQwXMMr3BQl8UUUXTA +ZhNBS9r7z+Epw79jPyW2lUttINtYaDrc1RmflT26BPUNxaAUoxYv//QOKkpMvkNB +hueQIGKoHdRTjcbVCwv18o9KtARk67M+3j0jyS7h5D/W3T/SQfIHBer2SmkJvkK5 +EeicPQy1g/nP6mLV+RGuYXBijZOADfcTHLkXhGmDNNgtYXgW02gV7XV6x6IxyGuD +80xHBH3oPwU3hyWwDVegKhZCF9NUv9sOwCpawG1RD+K/MczfsZTRc6cRjaXlYmll +OplhRGJ/cumFbTULaIhcsH6iG2zUEfsrTEtbXUmp/p4dQ1O++VpVUw3itUjLmaPZ +bI7tVb3PKwH0G16Y1anZmaRCjkOWtuBezFXDxn+ooHhlJE9H/hJZSu7b5pVcv+Me +PV2uaFPlkhfh9+kzhp8jBK64KQUOfWdQSdPHAA9uN/ZM9ZgSZoY4kTr5OuI76WoP +6Da5lcmglg9B9YIHwFa2wjguF9QFcF2e/8ooeGYRtuLCDYP9Ym6WMc9NqEjW4JQR +3F17BYHd0Dnj0WoepqhnToC2qVNqCT2gTDbTCk07qHR1IkDKmE7T9K582rNwjAzy +Sj+c7Rw2MBB9cZcGiu6U/ja7kGLDyhhS1YmYZ3luvWDR58Mc97DPGYofgDpIY3l6 +v02MQTgxf7VOlzczNEcYh3Y9RqfUS6KK7OyP/GH+YHoCfS/Rbp1jW4TI+Nam6Kjv +tl7m/TQH9tadWanLxTKLYq8zSPJNdpkDXWyl6LwCPKm2R5FBrweoExJo3M2eH6ln +WL08ymCW4mKd8ARGKEwfk5rKcFx4at5Myoqtdkuv9I1NBeDsgao4Lufi2whedOUe +rnWK/hJTvDRlTYz6cyGEpF8ksR5RFcQbJyoZJYFuuRNQFuESMa6WUAsIK1L7VGGs +l45UIu4FR+51J3KavXwZ1AcGw1eUSDzF+b5iEVhEvHrizPxd76qrFAzjM5SJ4eiM +Tf2EEHzwYfA1XVUoa4b0sqkEDa14ok2sdAR6HLzlhow6m84IVtI/euUVc2iuOJNR +dKUNAVoe8cM/uxyxOFvJIPL/epTppbEeKzPX7eJZPSvlnquhiQ/qzdHBfL9QLa2F +FvUnQr3zibDi3Kjn1uPh1Guc683wKU+C4wP0++KUFvI19z3XgUu7NIHnQUtgfMFE +olpjxudGQLDVr2x67VrL8mrvqxyzTpokwHy/2nkzB9rPlRacozCE/VALFFedsJT6 +UREgIXbAOh4e4WXVRXcCjXSPm+ydsrD9k0txjjFVUvSd2JzEYQvWvjuG0QDibny6 +nSVBmwXANslve/DQnHQTVzsrP6DL7h2m078aayLBWbJun+zQfOVYXGUthyPSZIDY +enegzKURe7u0gJlL3NCNLRj/aWxPZCr+D1phl18T2ES9mUJ8Ms8dAKpPusYxS8En +Hm2bYWZGrbQ3mGuVlBuvdJs2jS71a8X0tIDr1khDXGcTqAY2teLLFlXHlWbu5YJe +3/fDAgCV3q4s7Y4voUE8hTn9MuSJMzeKeTy8bcEMLtssdZTlp5kEdR/bHVSEQZGG +po7TiGfa4yCySfpc+/bglgd3q98dwe2Ouoq4EtmZkfnAU1dTbthnO1AUs77ymCy6 +V6ANPZALhq2qObQugcpinkDDZBdCnFj+nQLUwCyA7SSr8RqEUGxJpCJT6lpTL/rO +0MMXPVFtq9r9Y38+cCl+kRbecMNkJ21EjlLHb1s7wA6RiMJr3MzrasWkmParfHE+ +7hiAFdVtW7/sdRPo4BwAkIyK5vTZbi0jeRwF89IFdB/VlhOT3cJ0stXRVaNgfqP/ +vq9Ag9sSq+j7J2GFgdfZ8UZLteBEK9tHH8jwQIKfGqfpZXa2uknRaw3sX9S1EDhL +dYh9Le0CzPie/BRBtBJlulw2OnMJzT5bD4O7CRlrop4AOVqVA/vjlqZbCdvsF5o2 +oEymUUsPl8K4kdOmdH9E1/CgGjHgFb12cMMFTum83ElOJi28nR6nXuaizkd0NTxz +o47d0iZa6rv+oyJ0qZ4nO8SI8YpFUlkaWkX/WIB8Od3j9Dd8BM98vuR/xGnKElvr +OsxR9CxarGFdmF/Zua0vHJU3yoAwPpt6QpUQPPOscvC/L7ztG+mYvGQ3gA21u7n2 +2RXhr20M/htxgr9tP79iDUZ8dyVP7tgIATmeb1lzMOIWkxJ+S/hjCX13TKNREf3p +tNEFsjsep2HwOOKwZIYBq5fD6jrxyHY2d4LVYTYaDWwRL1gCEt2ILXXVZ4p2MRfY +qGFQAZglry7gfotoTDMfFNJ39LmoOoLM78k/vU1wUuNRMUvuikn8oJbVoPhtrnK9 +oAi43XBudv0GldxHu+hlImEbP4Hcz4AadJxNlo5lMEqAMP5tORMRTcp7WXV2XojE +sSk5J2JXM//kKkYqmtpoJ2EmDc4xCIPGAPGfan68ID8rV3Cd38IOKkaLzgLn5pxb +QibTuTkd7w+cqFIsG/ex1WZQW9I+UhROTYd9qfz/HGkQH+I9PleP1oHSZpacViPx +zON1uCvZksp2QoMMaR1iqW6A9ZL+psntexMePQ0gqReNHpdAAc6VTxVapsmH4Qeg +bbmenCXhh1FAc4gVGvjtv7fG0w92WxQTnKZAUsXUeRtFPcj6vkzqBqFwIGRREmmy +ZfkhpmNYarWSAGkmE/07NIjsHYuHLw4LkAKrVbUIHEoBvVnItIoBW2X1MhZDm9Ro +2c6p6DUvJ+XceZxS4y7jOImmDFHRadIZTLQ8D+yhg9jhdRf9Lodus8z7Xu6fjErm +i6n9fqbWpoirjXQIDXk/j37jaKGeZW0YtWoqjzYSaIKjybsPQWz5GHMioNRJnxHD +Bn3GIKkO2QdoGlwq80OI1O3bzq8/nqap3h6adiBjEWloxkYVjr5ieKpSLDomYMF3 +nS+XpyPCJe8h3uEbOfk0gay3hrEPE4HZk0RdKJDg/RMWKwheMCsB81O2IwpTdg0R +RC9QLtQUaUGS5hjJQfYPeP9b6Spio5j1g+y2OtQU8jZgoafq2cuYzicUUUmbJ13D +3P4opfobxBuFi0H0FubDnfOf93Y35Uh+dS4tNNbTOsn/Swnpk3/ZeCVErz9BISM5 +1insLN/3TLfK8LTVM/lOB8BfLQNkdXeZTM5E/rV+LXGrVkQf8M2pAf1QUWXmQ1h9 +tQy302ikaBC43A8SUhWDVRuLiW9tItIGCZKEqs6xDtPz7T1Jr2TwUeCtqmNxQaFW +F61bBz5uEBeVXOdb184/0yn499rOKUri1Gq+E4TVAy241KtDKEQ0bGTdQjI6mzLf +H6fxkt1pExYc8L/JzvyB4nwbaaE2gTM8cl9U5/vwmxPyDsFpYi91dInZjb5lwQ+0 +pxZsU0z9uLnkiO2bHK/WdyJpvYPR4zt2XCjwCl2EFYt64Ib/hhSJ6hH/fIm5W1/R +L5wpPIu/Dp4c0BsQW44wP7BPJkAWzJrDVHC0mMw1XbchQuBOnLtM3V+VHVAAM/M2 +95Wt97EZyEQGrx3+w1+DHbmPpZJADxhPBJiNjFhW8h4ekzXuVScC9oJprZoPPVYt +3C7OKRpPQbhkzVP/m8f3Bm+jhcD2gbNe40lsi5X4Qq6bzRhLJprU2XsVc2O5Fq8J +Uyj1BN4nKq3ZGXvu9A70uavjRY8ru8/jxAfckLgqnfrc2GBcTbx+1sPmxz1BwH7h +fNV2esyXI4sthJ1FsOpf8nqKYnsNfclScNzx48qqbbj205202jj/HFBuY8/gw1FH +b/hZUKT2QRcxo+JfeGkMQag9GxSJztENLkBCiMdg84Byyrvfqm/+eEGthOk0LJwz +yEKY5zHlqi9zItCrbNglq6yeNn7NuAlN9Jh+iLqGRwrcjDDZX1wUroeQ3IlK0u0W +VraBfM3bpXaoENqHxGR4Kr7l+dUMdvNLcMnnHPYsBrcIdsjOKdA3B6lnYvg5wK2s +gGTiXtJtePb01AEuWK3az8EghCCvEN9bhd7KMBA3sQXdGPUqrcKHagP/z9b/dbEQ ++KzcNykjUqlA/m5Xc0YsND3K7D01wwgU124e5LBfR12xs6n3Y5MJ0SXU9eAxWPwg +5JN0I6uKDu3hJeJsIbghEFrFNPfdNYPXZx78yrYO7dX1yylSfVK36gPeD5YYAOds +DxdujWuPTRHlaKTTUAGinYF2RHN6/ewQyQF1SWNHG7nXPlrGPAXT8r+UdGGwghmM +KElGesVmyIf0mb5WiyZ4dV0r1zc7SqL24qVKLfmOzjgxylH5JVdrhNzoBYTlaUZe +Zuz21vja3HZvGHOqpa55k7106ajVPfjcQdsRYqs8T4rlmu+YIeqL57GD7fj9O3r5 +buSCjGzWM+icFp5QamfFE1/o2W7LHZmK/3tT2kTh/+6wksRn6FVnmQWTOQb4wwIY +zIKuNnNPDjmxapUL07QmatlVs2XxQ9d7JfkpfzkKLNCC1tVGguowQvWxG5ChTfxf +5i0/mQQie5gVggPylqx5PQ3qtExZF/0VyL1QO3H4+dk3ksvpiWz7x6NZJjplancS +CIEYmV+0G4uYQe1bVMHOdBOkAcMbvcGSE+FKmFX0XljzGNosSfW24mozjp7j5OwE +SxVXwTELn4ocBjHwp4xUCZ/a7UGj4P7FReyOFMkHSyqrpeTfIteMCwL251v/KsgO +KP9otmx7ri7kN2YdkS+6z12wvXEiqvakCobbcZ9WDpTgmK1UWHe5GycmrhsGoA/0 +B+v08hOwMnn8amCKWkiC4H9ElU/LAQpSjNmzkeICtX7V+PIz7CpA1e8MrFdvgdzf +Sla44zXWpRYCXznlneDnJ3/7DwWkSFXma9/PE2ORMaFkDWus4AZa4wp+8LzXyS94 +rFz4dDSoh4NZ3oC4245yggcMNOVgf4nmSquM5qc+EOgV34nT/M2cJuQqMoHKKTnb +8caEl8YFWCj4VuCYgUxE6MQN5F1egqa4mkr0KSIS9oLqwTeC1dVkj6WDNyHVaiL+ +5a0Yf/RMf8ARjb7g8tM3O+2R1V59GuPGvZUOKLdma9wNn62e6wgg9UQCMZPzOck/ +NlQIi5Hs+kxeLE3n8ILVmwDZC0uZhhyLyFbSNZnYZ7lY8x6OQmAR0lbwM/+fGkh0 +tJZ+brC/z6etHyG75UXJqgl1tXcmm7tCIufbVYcWJ70ZG3dZ4u8ngiUuCqjGhX5z +r7JOU/gGkoDeTuLqL08eqKfd8FbaWEcvmkY/XPUdUOdDbHwI72axL3Iz8BqPhmM3 +dgN3YHfIK0gk/NP30LKL6JGs63HD7CvBtu9tC17rKlwTKUo5+VQZwNQtnI2Sl9t8 +/lnJppcHAftsye8b+RIHvQUu3Ev/y3J71eYMDbFBepLeoH/ufgNGbUt2h2dlfmZI +sNEvQ66rONz8gsBdRn38mYvpRGopFfLgjDaGokn5sJ+McEWROHWu1sjy4KRQv1j+ +skZOaMTCMd0cAyCwwatmpuQOsOAiM2XG9IiQ3LuJ5u1ixwFuWVmPnDfLQgYixJ9p +QcmAi333zKxmQHrLCLH+2leqI7TWn767pJTJuKsgGFRLsTWIOLFwaebxo0oq5JyL +QNkU5zQ3nI316i31qXx8WwKQohTRvM7LQ6poOVukibtzC3qEeIUGRYiKZbtAmFvH +Ff9pABj0V3FgeZfICQRpe1Bz5TDO4OXz7u8QnzZrKf1lvRp0ALtij9YwPXs/B9Or +De4xl9OEx4u7oaShCmUp0lcjHmNDpfPGE/L3GhsQ9f6PY6tQzmY1EhIIzomDT4pA +zCxjf7q9mETZoopbpU/d9nGbgMGJlXWMBI1SpcHkdorGR5EmPVwm3prJcLvEw2ew +C6Y6atpLvQHxra80zQvCFZJ6oTZeB3glYWiukjBFDJlbUXN5xxHDkng4UvOpSZvN +MsFOXpoEKedcMvSP++Nl0fchPdQo7zcrY4VoVoGcFVV0zHabFnK7157VSUYH3WIZ +HLTBrIQtClmfZqXhz+cTzDKUdLkZEGxrdCBGYF3Euo/4X6s1GRcqm1Lf4h/ho14Y +5m8q9fR9K71yPzzUpkOvzgpNirip4lxmFKJqely6rviWfkMeshvpre/2b4tOS/BF +huI7kvA7RdeyT+2zoogvfwbdFpMltu3XY4qLlZ5stzDVjY8EMOdOwUZ0uwZtbXn9 +v0+7GLSSlAsa5ikcttSgMj+Ico9sXSD+1kTfu0Ub4pB+Dqxr0lJrqBhhABuMxYqk +oNEdAk56DqGVy15mVs+M85Oat8Xxz7VtgWZomynMtTUwskZYcMQA2YC41GF92Rt7 +L4+dww0C80S6S3DzIq1F0AXBIVUqHYL5ZxWUiyQV1fqgTi4gZs7Z6Axjww+Eauf/ +UgwX7B+iJZMWfg0T9QBhsJAGKgB9vV/iZhkiaLS+2I7J1wybyq++2URa9Dq66gYp +JPtuNSKpSJf329qo6LUjUGBodpEes7sA99S8NCek1oTQ8NHKeGHX3UrkvECU4Vly +mjmVg8eSJzYFtbR7+hPurJHr9me4lj+szB0XwO6wJ7SVgeEn//cpGFTeJ7s+w6Yu +TOolk9MbSVdGviWZ36zDI1eaatg5jDHt0EsjvaS9S8TK74CPpcugzOLCfNKjwVJw +kKmAnATg8khtRYVMPI6X2/uL3LLS8Q2vJY0uv1SS2pCrha4MTRisZ0d7QdNNCzMj +C/DDAM5qzJ/xXUEz9/XFjkLMGfnvivEGuOrF3SaYLoTIXhf8iBuP0EBTkI6ljjDS +jfyeBIA8YQ2Iy8fFD5/PjrssemJBOims0bfNAJQocfCr6S/MARi5R0Rlg4ylqHXL +F2K0wiM/K4gpzl0AX65VRTZktMEaoyJ5hdvwyDMnyzv9cXePBjnb5YLuJPSdw10/ +8fdEWedqkZlgNSka7Yv3uY1hZzySCxYxB6FZ/PZRsSUvf07DLKGgm2Vw5bI9SOOR ++0gZ3YollK50HKOH61uQmmLl9ONwhSdvMnAgAFCT4hm7roYZcYVyH9rFaL7vb5Ih +i6ZTJidr1LqyY4+MbBvc0fJjuJ2YYhqUzg/IYp8KdmBbXDz79uqpjOkJQxWngQke +LBiMO63D/KRP9krtkdnyUH8O9luPp412csoxFADE3pKWcSEtiigmIOHm+YzU5H92 +UM/8A9gCFm4c/4+AEay/Gif9GYtaBHFWgTCHKfJ01Hzny5WhapaNDkpe9FExRxV1 +541fwKud2kKcVQNIgx0VQ+uinhnv+Nf9wv6WfiHYUtA0UJSj85yW7MO/GCViFydb +A1mHZ9owUOGrNdxX0smxJXkcM4aXZUfzeT5jjYa2SNFB+mX+biqgWP0BfUhAxEX0 +a1E/ojJzT7PTfXKxoUiVVZOlCgZU0saHJK6HIw2i45LruUGQOQgVKEAst36a7GVH +HX3UkPG2CIXO8X0pJ1QfCd0FndMyNcHPVa5z6yPs890FMjxTg6wZdpkMymnMeTW3 +/EJ+KRGMbfHn3h8eXcXEPyGVj6JfkAhbqsZGtgCByPWq/6NIhuLDTPDu+mzON/2u +Zbga4vPLm7/g+UKPXe9ZI8eIO79cRqfqJwVBZSst6Aoj11F5Ub67FlfHdba6xHIi +S21QS881+aRSu7oDPPa1kB/A+atdzm8NBOUFHvhNQ4jSCsMWcNaERICjrJas5Jjk +899bsJz/iX1sJnX/k1NYQzB6zKZIUhcjPob02LkROA9cD48R/gOB3KZBIYGs60Bt +NzO9H5oSNYhJH/7Bq5Tv+Gnvb42MWcHKqgYeb/1zVnW7IppDMCli7WKxbwfQxbkL +vI/E/0z5utGhuKAi8hEFIigbaXvtdjJlrHMru7S1WYWUHjU4TtkVnjByfkddf9wh +sjeNgWDUgdD7UOS+mOI+k5xXeAADDaCGZd85byFvMWfv7roWdcuhScheB6MzgY7d +q+yWbeWg7fyRtsQcIGc4tw0DE+OYE52Z2IJWTEq0UYX0bQqZNXgYswdD0HckqilE +0iRoZKJvmub0tEma2Ugqu51gx2RbNM7RLzlRXCupVS9JlYuKY3fpFT8QobGuA2Pw +dJDh6iM59TkQGf2fc9SpUs46SutJaSVRf+nNAfh9nDHcLN4hkWaVzz/23jVwBjzW +JAyPEPJGd0Y6TEZus4dP/ED43H9kzcXAVzocfxeoMGPmxWX+JAGcsqj1eUSx0Fj7 +SLlJEreQuVmzpOVo7xBB/7ZC5XYRKOxgDa6aAMg7d6jAT27L5TwTfmNpM9fKfMza +KhqWzncuvQ7ggTnjxQ184498HQP6wnwVkE92dKlNnA3XSpehhkEaqOoj5zAfWSSC +oghpsk2aSEVoJn7NZ8Ii5BDcgW2GnIMs3XZVWNmSByqxMLSCR3MvJzEgjcgKXSfV +wHpjfjfKnJoG6DUVhElk0ywXQEWN7JNJMx+8Xq5DIPFdYRKhjczqifPa0OhcJxQh +PPayhJk8AAmPXqHNW4qKOjfBzSjOKV+K3AfwUQgvfc9ZAiyaGIKJZQmZQtuMQtRb +0EiW2MsAlFyoDJzliZYO4+yqwieUfYPMInuW60rNW8cXKDBLZiC5gbLMSdA+kZZd +ATaJTH3sTN0+i0ZrU5MInP02ERfrXJzHvkn/QFxG1NYtqpP664n0rYyO5Dhq55cN +JCD7JOXs466Ids1mCMH5FyChw7u2aWGK06diUELk9sZyboLO/kw2R0q75AZ7OMhU +IDoMfawrp81WHKQ7r66FAmHMOca1Y2tzrLdf29INzCXqXztoSHQ6vmEM1iNEcyu2 +Wczp8DuC5v2gM88QcN6CiwriIxcFyYbgr8WTaVG33iK+JVYwORwMvUKppi32Ityx +zaybZtkYhfJJOi98sK8DFEFA5z3hB50MpVDHBs0cYMc66XHRs2HXOIYpRhvFCN0f +G7VAMSit3sJ8VfOheTZaGCwKaT3k8l8LTd6VyrBz2ZlxnmGXR/9gJfUFT+U0i9m8 +Etogz+ftf8xMW5o1oCHDOpzS8s369hdBmpqd92kcITjHZy0FxK709SnAVY42tNhZ +BUXVUvpqkcgP8GRWHP1o+eNGFNDGxtedSkoLzeFTITUFOh1STZuJmL/2VQ8IFSkM +KodxW495lNZ3gjOWmAMgelhrpQ9OHJcp9+iHat7Whp0RNrN2T6wjuEC9rnl+zHF8 +EhfYxn9iKpYeGADY2tV5EVE1EPDSD8byv0zNG0lq6adU+w5tae+5g1QKkXzyCs8e +ri5ac2T93Fho0WdAObZJV2JIeS8NfUA5bkogXYRQjEkCSSBKTx6LFUpwfTUdNQYT +z+IHu76MDe1aZD8EQiMfwC6hVtzH66yQtte4rG39nYoqJF8ws/NxWDNBzhv6g/Vi +KuNxO47KjOoYEJ64A9ACdjtt8F46wy4HhLzBCKKF/S2f9GYleOCHMmtAwGwL90sW +dA0zP9ZZT7SkYU03Nk0bcwu3W7LpldLbyYFoo2PjeBHGWEu7sUsf9w2WxaDxTXvx +vUZ0hBaFlZ6BVOyWivTaSEl/BOAAteZBPJ3hakY2r2dZaFkbp2JF2jPhD3e05eMR +cK4JCbn6QzBaHb/Zave7g+F3rfrDr8hCVlA6WSi1Mmb4mTMF9t1r+l9/PgF7iXgC +b7cfHje8r5bR/OrQIbM6BkfR5rRQVSzQHoxmUNBOSjXybRnLr9ZCuY8krU1eZ33l +3nRLER3E1ao2NuQWORHwcwzGWyr1GNR3h8uJ6dXqlj4biBVcwHr6qLdkcoolb+1N +2NIcqmvD9C/m/YcdCNf99RNBdGuT7o3JPGV8lcWvGBA3SG7fH0RaUm098K8B7sDZ +m8aOiNsJx2UoX1lTVOG2CeSYrHKomB0rMtPa7Dczi0eBgLBswqIRNfJulQnLZyGB ++nFq+Dy5puMs26Zgzx0UzgI+JDoyFefAve/V8cGZnT2iBarzZGL5unqPxshq2r+a +e+HswcgpFlCuHEgRPDpCuodcWRoZ+V9V5qdid/H5yzFqzAkgUbsxZvsyBjoN3Dq6 +1+7fArlKVEYCIDHyIV2D8EHBrZErbnTPPcLVgv4eAM514aOpzMJpJbHeTkuWuDRt +y/sHtjHklXcBrgFODjN0Rd6LZmLza3fWYq8dERFCeJpBD06xR6AvZWEQegMBwDkW +y0ewgozAdtcKQjJ9FeNPbzh50rTTC4HEhpHcq6nVj80JzuxjoiaMKP/V2vmt/LCI +9S2gySRpNMCrhrpbOcpiW8i5ZZdFOFtEGgd54hPedyG7LTTbVaKNsTSs0UGdUlKJ +Src4YU5Et6yHaS3JCpIPV6fia89UKF6b+LQCdNGDVrNJLlyvMqrxSUYnG5lv8W3X +7nQZAZMhwgOq7m+uGH87o2H814X/9z9JpEbXwIgcw7nuTQ65H44F8pLKwvQg1fMW +y7wownaEA+iu5FH+mXDtMddPeHC/jFwe6Ky58EVWE0me9I9Lmi1ImB3p4116Bujm +3ptybxOCYN+8x5BPjsO+i2Mmke1FsM4CK/+L0430h5BET2LDqyTUVbWgcxSEC1Uc +7pa2HSH3ZFmSC8g5gt5ZA3TSQv8iY/7xD5cMsbk= +=6HOm +-----END PGP MESSAGE----- diff --git a/propellor.cabal b/propellor.cabal index 2ac8a44..50067b8 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -1,5 +1,5 @@ Name: propellor -Version: 0.7.0 +Version: 0.8.1 Cabal-Version: >= 1.6 License: BSD3 Maintainer: Joey Hess @@ -25,7 +25,6 @@ Extra-Source-Files: debian/copyright debian/rules debian/lintian-overrides - .gitignore Synopsis: property-based host configuration management in haskell Description: Propellor enures that the system it's run in satisfies a list of @@ -81,6 +80,7 @@ Library Propellor.Property.File Propellor.Property.Git Propellor.Property.Gpg + Propellor.Property.Grub Propellor.Property.Network Propellor.Property.Obnam Propellor.Property.OpenId @@ -92,8 +92,9 @@ Library Propellor.Property.Sudo Propellor.Property.Tor Propellor.Property.User - Propellor.Property.HostingProvider.DigitalOcean Propellor.Property.HostingProvider.CloudAtCost + Propellor.Property.HostingProvider.DigitalOcean + Propellor.Property.HostingProvider.Linode Propellor.Property.SiteSpecific.GitHome Propellor.Property.SiteSpecific.JoeySites Propellor.Property.SiteSpecific.GitAnnexBuilder @@ -105,6 +106,7 @@ Library Propellor.Types Propellor.Types.OS Propellor.Types.Dns + Propellor.Types.PrivData Other-Modules: Propellor.Types.Info Propellor.CmdLine @@ -126,6 +128,7 @@ Library Utility.Process Utility.SafeCommand Utility.Scheduled + Utility.Table Utility.ThreadScheduler Utility.Tmp Utility.UserInfo diff --git a/src/Propellor/CmdLine.hs b/src/Propellor/CmdLine.hs index 32e9731..7b39cd2 100644 --- a/src/Propellor/CmdLine.hs +++ b/src/Propellor/CmdLine.hs @@ -26,9 +26,11 @@ usage = do , " propellor" , " propellor hostname" , " propellor --spin hostname" - , " propellor --set hostname field" - , " propellor --dump hostname field" , " propellor --add-key keyid" + , " propellor --set field context" + , " propellor --dump field context" + , " propellor --edit field context" + , " propellor --list-fields" ] exitFailure @@ -39,8 +41,10 @@ processCmdLine = go =<< getArgs go ("--spin":h:[]) = return $ Spin h go ("--boot":h:[]) = return $ Boot h go ("--add-key":k:[]) = return $ AddKey k - go ("--set":h:f:[]) = withprivfield f (return . Set h) - go ("--dump":h:f:[]) = withprivfield f (return . Dump h) + go ("--set":f:c:[]) = withprivfield f c Set + go ("--dump":f:c:[]) = withprivfield f c Dump + go ("--edit":f:c:[]) = withprivfield f c Edit + go ("--list-fields":[]) = return ListFields go ("--continue":s:[]) = case readish s of Just cmdline -> return $ Continue cmdline Nothing -> errorMessage "--continue serialization failure" @@ -56,8 +60,8 @@ processCmdLine = go =<< getArgs else return $ Run s go _ = usage - withprivfield s f = case readish s of - Just pf -> f pf + withprivfield s c f = case readish s of + Just pf -> return $ f pf (Context c) Nothing -> errorMessage $ "Unknown privdata field " ++ s defaultMain :: [Host] -> IO () @@ -69,8 +73,10 @@ defaultMain hostlist = do go True cmdline where go _ (Continue cmdline) = go False cmdline - go _ (Set hn field) = setPrivData hn field - go _ (Dump hn field) = dumpPrivData hn field + go _ (Set field context) = setPrivData field context + go _ (Dump field context) = dumpPrivData field context + go _ (Edit field context) = editPrivData field context + go _ ListFields = listPrivDataFields hostlist go _ (AddKey keyid) = addKey keyid go _ (Chain hn) = withhost hn $ \h -> do r <- runPropellor h $ ensureProperties $ hostProperties h @@ -78,7 +84,7 @@ defaultMain hostlist = do go _ (Docker hn) = Docker.chain hn go True cmdline@(Spin _) = buildFirst cmdline $ go False cmdline go True cmdline = updateFirst cmdline $ go False cmdline - go False (Spin hn) = withhost hn $ const $ spin hn + go False (Spin hn) = withhost hn $ spin hn go False (Run hn) = ifM ((==) 0 <$> getRealUserID) ( onlyProcess $ withhost hn mainProperties , go True (Spin hn) @@ -86,7 +92,7 @@ defaultMain hostlist = do go False (Boot hn) = onlyProcess $ withhost hn boot withhost :: HostName -> (Host -> IO ()) -> IO () - withhost hn a = maybe (unknownhost hn) a (findHost hostlist hn) + withhost hn a = maybe (unknownhost hn hostlist) a (findHost hostlist hn) onlyProcess :: IO a -> IO a onlyProcess a = bracket lock unlock (const a) @@ -100,11 +106,12 @@ onlyProcess a = bracket lock unlock (const a) alreadyrunning = error "Propellor is already running on this host!" lockfile = localdir ".lock" -unknownhost :: HostName -> IO a -unknownhost h = errorMessage $ unlines +unknownhost :: HostName -> [Host] -> IO a +unknownhost h hosts = errorMessage $ unlines [ "Propellor does not know about host: " ++ h , "(Perhaps you should specify the real hostname on the command line?)" , "(Or, edit propellor's config.hs to configure this host)" + , "Known hosts: " ++ unwords (map hostName hosts) ] buildFirst :: CmdLine -> IO () -> IO () @@ -170,17 +177,19 @@ updateFirst cmdline next = do getCurrentGitSha1 :: String -> IO String getCurrentGitSha1 branchref = readProcess "git" ["show-ref", "--hash", branchref] -spin :: HostName -> IO () -spin hn = do +spin :: HostName -> Host -> IO () +spin hn hst = do url <- getUrl void $ gitCommit [Param "--allow-empty", Param "-a", Param "-m", Param "propellor spin"] void $ boolSystem "git" [Param "push"] cacheparams <- toCommand <$> sshCachingParams hn - go cacheparams url =<< gpgDecrypt (privDataFile hn) + go cacheparams url =<< hostprivdata where + hostprivdata = show . filterPrivData hst <$> decryptPrivData + go cacheparams url privdata = withBothHandles createProcessSuccess (proc "ssh" $ cacheparams ++ [user, bootstrapcmd]) $ \(toh, fromh) -> do let finish = do - senddata toh (privDataFile hn) privDataMarker privdata + senddata toh "privdata" privDataMarker privdata hClose toh -- Display remaining output. @@ -201,7 +210,8 @@ spin hn = do bootstrapcmd = shellWrap $ intercalate " ; " [ "if [ ! -d " ++ localdir ++ " ]" , "then " ++ intercalate " && " - [ "apt-get --no-install-recommends --no-upgrade -y install git make" + [ "apt-get update" + , "apt-get --no-install-recommends --no-upgrade -y install git make" , "echo " ++ toMarked statusMarker (show NeedGitClone) ] , "else " ++ intercalate " && " @@ -222,8 +232,8 @@ spin hn = do Just status -> return status showremote s = putStrLn s - senddata toh f marker s = void $ - actionMessage ("Sending " ++ f ++ " (" ++ show (length s) ++ " bytes) to " ++ hn) $ do + senddata toh desc marker s = void $ + actionMessage ("Sending " ++ desc ++ " (" ++ show (length s) ++ " bytes) to " ++ hn) $ do sendMarked toh marker s return True diff --git a/src/Propellor/PrivData.hs b/src/Propellor/PrivData.hs index 5ddbdcf..f85ded1 100644 --- a/src/Propellor/PrivData.hs +++ b/src/Propellor/PrivData.hs @@ -2,18 +2,23 @@ module Propellor.PrivData where -import qualified Data.Map as M import Control.Applicative import System.FilePath import System.IO import System.Directory import Data.Maybe +import Data.Monoid import Data.List import Control.Monad +import Control.Monad.IfElse import "mtl" Control.Monad.Reader +import qualified Data.Map as M +import qualified Data.Set as S import Propellor.Types +import Propellor.Types.Info import Propellor.Message +import Propellor.Info import Utility.Monad import Utility.PartialPrelude import Utility.Exception @@ -21,53 +26,123 @@ import Utility.Process import Utility.Tmp import Utility.SafeCommand import Utility.Misc +import Utility.FileMode +import Utility.Env +import Utility.Table --- | When the specified PrivDataField is available on the host Propellor --- is provisioning, it provies the data to the action. Otherwise, it prints --- a message to help the user make the necessary private data available. -withPrivData :: PrivDataField -> (String -> Propellor Result) -> Propellor Result -withPrivData field a = maybe missing a =<< liftIO (getPrivData field) +-- | Allows a Property to access the value of a specific PrivDataField, +-- for use in a specific Context. +-- +-- Example use: +-- +-- > withPrivData (PrivFile pemfile) (Context "joeyh.name") $ \getdata -> +-- > property "joeyh.name ssl cert" $ getdata $ \privdata -> +-- > liftIO $ writeFile pemfile privdata +-- > where pemfile = "/etc/ssl/certs/web.pem" +-- +-- Note that if the value is not available, the action is not run +-- and instead it prints a message to help the user make the necessary +-- private data available. +-- +-- The resulting Property includes Info about the PrivDataField +-- being used, which is necessary to ensure that the privdata is sent to +-- the remote host by propellor. +withPrivData + :: PrivDataField + -> Context + -> (((PrivData -> Propellor Result) -> Propellor Result) -> Property) + -> Property +withPrivData field context@(Context cname) mkprop = addinfo $ mkprop $ \a -> + maybe missing a =<< liftIO (getLocalPrivData field context) where - missing = do - host <- asks hostName - let host' = if ".docker" `isSuffixOf` host - then "$parent_host" - else host - liftIO $ do - warningMessage $ "Missing privdata " ++ show field - putStrLn $ "Fix this by running: propellor --set "++host'++" '" ++ show field ++ "'" - return FailedChange + missing = liftIO $ do + warningMessage $ "Missing privdata " ++ show field ++ " (for " ++ cname ++ ")" + putStrLn $ "Fix this by running: propellor --set '" ++ show field ++ "' '" ++ cname ++ "'" + return FailedChange + addinfo p = p { propertyInfo = propertyInfo p <> mempty { _privDataFields = S.singleton (field, context) } } -getPrivData :: PrivDataField -> IO (Maybe String) -getPrivData field = do - m <- catchDefaultIO Nothing $ readish <$> readFile privDataLocal - return $ maybe Nothing (M.lookup field) m +addPrivDataField :: (PrivDataField, Context) -> Property +addPrivDataField v = pureInfoProperty (show v) $ + mempty { _privDataFields = S.singleton v } -setPrivData :: HostName -> PrivDataField -> IO () -setPrivData host field = do +{- Gets the requested field's value, in the specified context if it's + - available, from the host's local privdata cache. -} +getLocalPrivData :: PrivDataField -> Context -> IO (Maybe PrivData) +getLocalPrivData field context = + getPrivData field context . fromMaybe M.empty <$> localcache + where + localcache = catchDefaultIO Nothing $ readish <$> readFile privDataLocal + +type PrivMap = M.Map (PrivDataField, Context) PrivData + +{- Get only the set of PrivData that the Host's Info says it uses. -} +filterPrivData :: Host -> PrivMap -> PrivMap +filterPrivData host = M.filterWithKey (\k _v -> S.member k used) + where + used = _privDataFields $ hostInfo host + +getPrivData :: PrivDataField -> Context -> PrivMap -> Maybe PrivData +getPrivData field context = M.lookup (field, context) + +setPrivData :: PrivDataField -> Context -> IO () +setPrivData field context = do putStrLn "Enter private data on stdin; ctrl-D when done:" - value <- chomp <$> hGetContentsStrict stdin + setPrivDataTo field context =<< hGetContentsStrict stdin + +dumpPrivData :: PrivDataField -> Context -> IO () +dumpPrivData field context = + maybe (error "Requested privdata is not set.") putStrLn + =<< (getPrivData field context <$> decryptPrivData) + +editPrivData :: PrivDataField -> Context -> IO () +editPrivData field context = do + v <- getPrivData field context <$> decryptPrivData + v' <- withTmpFile "propellorXXXX" $ \f h -> do + hClose h + maybe noop (writeFileProtected f) v + editor <- getEnvDefault "EDITOR" "vi" + unlessM (boolSystem editor [File f]) $ + error "Editor failed; aborting." + readFile f + setPrivDataTo field context v' + +listPrivDataFields :: [Host] -> IO () +listPrivDataFields hosts = do + m <- decryptPrivData + showtable "Currently set data:" $ + map mkrow (M.keys m) + showtable "Data that would be used if set:" $ + map mkrow (M.keys $ M.difference wantedmap m) + where + header = ["Field", "Context", "Used by"] + mkrow k@(field, (Context context)) = + [ shellEscape $ show field + , shellEscape context + , intercalate ", " $ sort $ fromMaybe [] $ M.lookup k usedby + ] + mkhostmap host = M.fromList $ map (\k -> (k, [hostName host])) $ + S.toList $ _privDataFields $ hostInfo host + usedby = M.unionsWith (++) $ map mkhostmap hosts + wantedmap = M.fromList $ zip (M.keys usedby) (repeat "") + showtable desc rows = do + putStrLn $ "\n" ++ desc + putStr $ unlines $ formatTable $ tableWithHeader header rows + +setPrivDataTo :: PrivDataField -> Context -> PrivData -> IO () +setPrivDataTo field context value = do makePrivDataDir - let f = privDataFile host - m <- decryptPrivData host - let m' = M.insert field value m - gpgEncrypt f (show m') + m <- decryptPrivData + let m' = M.insert (field, context) (chomp value) m + gpgEncrypt privDataFile (show m') putStrLn "Private data set." - void $ boolSystem "git" [Param "add", File f] + void $ boolSystem "git" [Param "add", File privDataFile] where chomp s | end s == "\n" = chomp (beginning s) | otherwise = s -dumpPrivData :: HostName -> PrivDataField -> IO () -dumpPrivData host field = go . M.lookup field =<< decryptPrivData host - where - go Nothing = error "Requested privdata is not set." - go (Just s) = putStrLn s - -decryptPrivData :: HostName -> IO (M.Map PrivDataField String) -decryptPrivData host = fromMaybe M.empty . readish - <$> gpgDecrypt (privDataFile host) +decryptPrivData :: IO PrivMap +decryptPrivData = fromMaybe M.empty . readish <$> gpgDecrypt privDataFile makePrivDataDir :: IO () makePrivDataDir = createDirectoryIfMissing False privDataDir @@ -75,8 +150,8 @@ makePrivDataDir = createDirectoryIfMissing False privDataDir privDataDir :: FilePath privDataDir = "privdata" -privDataFile :: HostName -> FilePath -privDataFile host = privDataDir host ++ ".gpg" +privDataFile :: FilePath +privDataFile = privDataDir "privdata.gpg" privDataLocal :: FilePath privDataLocal = privDataDir "local" diff --git a/src/Propellor/Property/Docker.hs b/src/Propellor/Property/Docker.hs index 1521eb6..4307b85 100644 --- a/src/Propellor/Property/Docker.hs +++ b/src/Propellor/Property/Docker.hs @@ -55,10 +55,11 @@ installed = Apt.installed ["docker.io"] -- | Configures docker with an authentication file, so that images can be -- pushed to index.docker.io. Optional. configured :: Property -configured = property "docker configured" go `requires` installed +configured = prop `requires` installed where - go = withPrivData DockerAuthentication $ \cfg -> ensureProperty $ - "/root/.dockercfg" `File.hasContent` (lines cfg) + prop = withPrivData DockerAuthentication anyContext $ \getcfg -> + property "docker configured" $ getcfg $ \cfg -> ensureProperty $ + "/root/.dockercfg" `File.hasContent` (lines cfg) -- | A short descriptive name for a container. -- Should not contain whitespace or other unusual characters, @@ -86,8 +87,8 @@ cn2hn cn = cn ++ ".docker" -- The container has its own Properties which are handled by running -- propellor inside the container. -- --- Additionally, the container can have DNS info, such as a CNAME. --- These become info of the host(s) it's docked in. +-- When the container's Properties include DNS info, such as a CNAME, +-- that is propigated to the Info of the host(s) it's docked in. -- -- Reverting this property ensures that the container is stopped and -- removed. @@ -96,7 +97,7 @@ docked -> ContainerName -> RevertableProperty docked hosts cn = RevertableProperty - ((maybe id exposeDnsInfos mhost) (go "docked" setup)) + ((maybe id propigateInfo mhost) (go "docked" setup)) (go "undocked" teardown) where go desc a = property (desc ++ " " ++ cn) $ do @@ -123,9 +124,12 @@ docked hosts cn = RevertableProperty ] ] -exposeDnsInfos :: Host -> Property -> Property -exposeDnsInfos (Host _ _ containerinfo) p = combineProperties (propertyDesc p) $ - p : map addDNS (S.toList $ _dns containerinfo) +propigateInfo :: Host -> Property -> Property +propigateInfo (Host _ _ containerinfo) p = + combineProperties (propertyDesc p) $ p : dnsprops ++ privprops + where + dnsprops = map addDNS (S.toList $ _dns containerinfo) + privprops = map addPrivDataField (S.toList $ _privDataFields containerinfo) findContainer :: Maybe Host @@ -390,7 +394,7 @@ chain s = case toContainerId s of -- being run. So, retry connections to the client for up to -- 1 minute. provisionContainer :: ContainerId -> Property -provisionContainer cid = containerDesc cid $ property "provision" $ liftIO $ do +provisionContainer cid = containerDesc cid $ property "provisioned" $ liftIO $ do let shim = Shim.file (localdir "propellor") (localdir shimdir cid) r <- simpleShClientRetry 60 (namedPipe cid) shim params (go Nothing) when (r /= FailedChange) $ diff --git a/src/Propellor/Property/File.hs b/src/Propellor/Property/File.hs index 0b06017..0e738f2 100644 --- a/src/Propellor/Property/File.hs +++ b/src/Propellor/Property/File.hs @@ -17,16 +17,17 @@ f `hasContent` newcontent = fileProperty ("replace " ++ f) -- -- The file's permissions are preserved if the file already existed. -- Otherwise, they're set to 600. -hasPrivContent :: FilePath -> Property -hasPrivContent f = property desc $ withPrivData (PrivFile f) $ \privcontent -> - ensureProperty $ fileProperty' writeFileProtected desc - (\_oldcontent -> lines privcontent) f +hasPrivContent :: FilePath -> Context -> Property +hasPrivContent f context = withPrivData (PrivFile f) context $ \getcontent -> + property desc $ getcontent $ \privcontent -> + ensureProperty $ fileProperty' writeFileProtected desc + (\_oldcontent -> lines privcontent) f where desc = "privcontent " ++ f -- | Leaves the file world-readable. -hasPrivContentExposed :: FilePath -> Property -hasPrivContentExposed f = hasPrivContent f `onChange` +hasPrivContentExposed :: FilePath -> Context -> Property +hasPrivContentExposed f context = hasPrivContent f context `onChange` mode f (combineModes (ownerWriteMode:readModes)) -- | Ensures that a line is present in a file, adding it to the end if not. diff --git a/src/Propellor/Property/Gpg.hs b/src/Propellor/Property/Gpg.hs index 64ea9fe..b469866 100644 --- a/src/Propellor/Property/Gpg.hs +++ b/src/Propellor/Property/Gpg.hs @@ -9,6 +9,8 @@ import System.PosixCompat installed :: Property installed = Apt.installed ["gnupg"] +type GpgKeyId = String + -- | Sets up a user with a gpg key from the privdata. -- -- Note that if a secret key is exported using gpg -a --export-secret-key, @@ -21,19 +23,20 @@ installed = Apt.installed ["gnupg"] -- The GpgKeyId does not have to be a numeric id; it can just as easily -- be a description of the key. keyImported :: GpgKeyId -> UserName -> Property -keyImported keyid user = flagFile' (property desc go) genflag +keyImported keyid user = flagFile' prop genflag `requires` installed where desc = user ++ " has gpg key " ++ show keyid genflag = do d <- dotDir user return $ d ".propellor-imported-keyid-" ++ keyid - go = withPrivData (GpgKey keyid) $ \key -> makeChange $ - withHandle StdinHandle createProcessSuccess - (proc "su" ["-c", "gpg --import", user]) $ \h -> do - fileEncoding h - hPutStr h key - hClose h + prop = withPrivData GpgKey (Context keyid) $ \getkey -> + property desc $ getkey $ \key -> makeChange $ + withHandle StdinHandle createProcessSuccess + (proc "su" ["-c", "gpg --import", user]) $ \h -> do + fileEncoding h + hPutStr h key + hClose h dotDir :: UserName -> IO FilePath dotDir user = do diff --git a/src/Propellor/Property/Grub.hs b/src/Propellor/Property/Grub.hs new file mode 100644 index 0000000..841861f --- /dev/null +++ b/src/Propellor/Property/Grub.hs @@ -0,0 +1,39 @@ +module Propellor.Property.Grub where + +import Propellor +import qualified Propellor.Property.File as File +import qualified Propellor.Property.Apt as Apt + +-- | Eg, hd0,0 or xen/xvda1 +type GrubDevice = String + +type TimeoutSecs = Int + +-- | Use PV-grub chaining to boot +-- +-- Useful when the VPS's pv-grub is too old to boot a modern kernel image. +-- +-- http://notes.pault.ag/linode-pv-grub-chainning/ +-- +-- The rootdev should be in the form "hd0", while the bootdev is in the form +-- "xen/xvda". +chainPVGrub :: GrubDevice -> GrubDevice -> TimeoutSecs -> Property +chainPVGrub rootdev bootdev timeout = combineProperties desc + [ File.dirExists "/boot/grub" + , "/boot/grub/menu.lst" `File.hasContent` + [ "default 1" + , "timeout " ++ show timeout + , "" + , "title grub-xen shim" + , "root (" ++ rootdev ++ ")" + , "kernel /boot/xen-shim" + , "boot" + ] + , "/boot/load.cf" `File.hasContent` + [ "configfile (" ++ bootdev ++ ")/boot/grub/grub.cfg" ] + , Apt.installed ["grub-xen"] + , flagFile (scriptProperty ["update-grub; grub-mkimage --prefix '(" ++ bootdev ++ ")/boot/grub' -c /boot/load.cf -O x86_64-xen /usr/lib/grub/x86_64-xen/*.mod > /boot/xen-shim"]) "/boot/xen-shim" + `describe` "/boot-xen-shim" + ] + where + desc = "chain PV-grub" diff --git a/src/Propellor/Property/HostingProvider/Linode.hs b/src/Propellor/Property/HostingProvider/Linode.hs new file mode 100644 index 0000000..34d7218 --- /dev/null +++ b/src/Propellor/Property/HostingProvider/Linode.hs @@ -0,0 +1,10 @@ +module Propellor.Property.HostingProvider.Linode where + +import Propellor +import qualified Propellor.Property.Grub as Grub + +-- | Linode's pv-grub-x86_64 does not currently support booting recent +-- Debian kernels compressed with xz. This sets up pv-grub chaing to enable +-- it. +chainPVGrub :: Grub.TimeoutSecs -> Property +chainPVGrub = Grub.chainPVGrub "hd0" "xen/xvda" diff --git a/src/Propellor/Property/Hostname.hs b/src/Propellor/Property/Hostname.hs index 10fda04..1cce4e6 100644 --- a/src/Propellor/Property/Hostname.hs +++ b/src/Propellor/Property/Hostname.hs @@ -3,11 +3,16 @@ module Propellor.Property.Hostname where import Propellor import qualified Propellor.Property.File as File --- | Ensures that the hostname is set to the HostInfo value. +-- | Ensures that the hostname is set using best practices. +-- -- Configures /etc/hostname and the current hostname. -- --- A FQDN also configures /etc/hosts, with an entry for 127.0.1.1, which is --- standard at least on Debian to set the FDQN (127.0.0.1 is localhost). +-- /etc/hosts is also configured, with an entry for 127.0.1.1, which is +-- standard at least on Debian to set the FDQN. +-- +-- Also, the /etc/hosts 127.0.0.1 line is set to localhost. Putting any +-- other hostnames there is not best practices and can lead to annoying +-- messages from eg, apache. sane :: Property sane = property ("sane hostname") (ensureProperty . setTo =<< asks hostName) @@ -21,13 +26,14 @@ setTo hn = combineProperties desc go [ Just $ "/etc/hostname" `File.hasContent` [basehost] , if null domain then Nothing - else Just $ File.fileProperty desc - addhostline "/etc/hosts" + else Just $ trivial $ hostsline "127.0.1.1" [hn, basehost] + , Just $ trivial $ hostsline "127.0.0.1" ["localhost"] , Just $ trivial $ cmdProperty "hostname" [basehost] ] - hostip = "127.0.1.1" - hostline = hostip ++ "\t" ++ hn ++ " " ++ basehost - - addhostline ls = hostline : filter (not . hashostip) ls - hashostip l = headMaybe (words l) == Just hostip + hostsline ip names = File.fileProperty desc + (addhostsline ip names) + "/etc/hosts" + addhostsline ip names ls = + (ip ++ "\t" ++ (unwords names)) : filter (not . hasip ip) ls + hasip ip l = headMaybe (words l) == Just ip diff --git a/src/Propellor/Property/OpenId.hs b/src/Propellor/Property/OpenId.hs index 051d642..39cb6ff 100644 --- a/src/Propellor/Property/OpenId.hs +++ b/src/Propellor/Property/OpenId.hs @@ -25,5 +25,6 @@ providerFor users baseurl = propertyList desc $ -- the identitites directory controls access, so open up -- file mode - identfile u = File.hasPrivContentExposed $ - concat $ [ "/var/lib/simpleid/identities/", u, ".identity" ] + identfile u = File.hasPrivContentExposed + (concat [ "/var/lib/simpleid/identities/", u, ".identity" ]) + (Context baseurl) diff --git a/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs index 85584e4..4cb26a5 100644 --- a/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs +++ b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs @@ -23,29 +23,25 @@ builddir = gitbuilderdir "build" type TimeOut = String -- eg, 5h -autobuilder :: CronTimes -> TimeOut -> Bool -> Property -autobuilder crontimes timeout rsyncupload = combineProperties "gitannexbuilder" +autobuilder :: Architecture -> CronTimes -> TimeOut -> Property +autobuilder arch crontimes timeout = combineProperties "gitannexbuilder" [ Apt.serviceInstalledRunning "cron" , Cron.niceJob "gitannexbuilder" crontimes builduser gitbuilderdir $ "git pull ; timeout " ++ timeout ++ " ./autobuild" -- The builduser account does not have a password set, -- instead use the password privdata to hold the rsync server -- password used to upload the built image. - , property "rsync password" $ do - let f = homedir "rsyncpassword" - if rsyncupload - then withPrivData (Password builduser) $ \p -> do - oldp <- liftIO $ catchDefaultIO "" $ - readFileStrict f - if p /= oldp - then makeChange $ writeFile f p - else noChange - else do - ifM (liftIO $ doesFileExist f) - ( noChange - , makeChange $ writeFile f "no password configured" - ) + , withPrivData (Password builduser) context $ \getpw -> + property "rsync password" $ getpw $ \pw -> do + oldpw <- liftIO $ catchDefaultIO "" $ + readFileStrict pwfile + if pw /= oldpw + then makeChange $ writeFile pwfile pw + else noChange ] + where + context = Context ("gitannexbuilder " ++ arch) + pwfile = homedir "rsyncpassword" tree :: Architecture -> Property tree buildarch = combineProperties "gitannexbuilder tree" @@ -101,13 +97,13 @@ standardAutoBuilderContainer dockerImage arch buildminute timeout = Docker.conta & User.accountFor builduser & tree arch & buildDepsApt - & autobuilder (show buildminute ++ " * * * *") timeout True + & autobuilder arch (show buildminute ++ " * * * *") timeout androidAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Host androidAutoBuilderContainer dockerImage crontimes timeout = androidContainer dockerImage "android-git-annex-builder" (tree "android") builddir & Apt.unattendedUpgrades - & autobuilder crontimes timeout True + & autobuilder "android" crontimes timeout -- Android is cross-built in a Debian i386 container, using the Android NDK. androidContainer :: (System -> Docker.Image) -> Docker.ContainerName -> Property -> FilePath -> Host @@ -154,7 +150,7 @@ armelCompanionContainer dockerImage = Docker.container "armel-git-annex-builder- -- The armel builder can ssh to this companion. & Docker.expose "22" & Apt.serviceInstalledRunning "ssh" - & Ssh.authorizedKeys builduser + & Ssh.authorizedKeys builduser (Context "armel-git-annex-builder") armelAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Host armelAutoBuilderContainer dockerImage crontimes timeout = Docker.container "armel-git-annex-builder" @@ -172,9 +168,9 @@ armelAutoBuilderContainer dockerImage crontimes timeout = Docker.container "arme -- git-annex/standalone/linux/install-haskell-packages -- which is not fully automated.) & buildDepsNoHaskellLibs - & autobuilder crontimes timeout True + & autobuilder "armel" crontimes timeout `requires` tree "armel" - & Ssh.keyImported SshRsa builduser + & Ssh.keyImported SshRsa builduser (Context "armel-git-annex-builder") & trivial writecompanionaddress where writecompanionaddress = scriptProperty diff --git a/src/Propellor/Property/SiteSpecific/JoeySites.hs b/src/Propellor/Property/SiteSpecific/JoeySites.hs index 120ea61..c770907 100644 --- a/src/Propellor/Property/SiteSpecific/JoeySites.hs +++ b/src/Propellor/Property/SiteSpecific/JoeySites.hs @@ -16,6 +16,7 @@ import qualified Propellor.Property.Obnam as Obnam import qualified Propellor.Property.Apache as Apache import Utility.SafeCommand import Utility.FileMode +import Utility.Path import Data.List import System.Posix.Files @@ -28,7 +29,7 @@ oldUseNetServer hosts = propertyList ("olduse.net server") [ "--repository=sftp://2318@usw-s002.rsync.net/~/olduse.net" , "--client-name=spool" ] Obnam.OnlyClient - `requires` Ssh.keyImported SshRsa "root" + `requires` Ssh.keyImported SshRsa "root" (Context "olduse.net") `requires` Ssh.knownHost hosts "usw-s002.rsync.net" "root" , check (not . isSymbolicLink <$> getSymbolicLinkStatus newsspool) $ property "olduse.net spool in place" $ makeChange $ do @@ -84,37 +85,44 @@ oldUseNetInstalled pkg = check (not <$> Apt.isInstalled pkg) $ , "dpkg -i ../" ++ pkg ++ "_*.deb || true" , "apt-get -fy install" -- dependencies , "rm -rf /root/tmp/oldusenet" + -- screen fails unless the directory has this mode. + -- not sure what's going on. + , "chmod 777 /var/run/screen" ] `describe` "olduse.net built" ] kgbServer :: Property -kgbServer = withOS desc $ \o -> case o of - (Just (System (Debian Unstable) _)) -> - ensureProperty $ propertyList desc - [ Apt.serviceInstalledRunning "kgb-bot" - , File.hasPrivContent "/etc/kgb-bot/kgb.conf" - `onChange` Service.restarted "kgb-bot" - , "/etc/default/kgb-bot" `File.containsLine` "BOT_ENABLED=1" - `describe` "kgb bot enabled" - `onChange` Service.running "kgb-bot" - ] - _ -> error "kgb server needs Debian unstable (for kgb-bot 1.31+)" +kgbServer = propertyList desc + [ withOS desc $ \o -> case o of + (Just (System (Debian Unstable) _)) -> + ensureProperty $ propertyList desc + [ Apt.serviceInstalledRunning "kgb-bot" + , "/etc/default/kgb-bot" `File.containsLine` "BOT_ENABLED=1" + `describe` "kgb bot enabled" + `onChange` Service.running "kgb-bot" + ] + _ -> error "kgb server needs Debian unstable (for kgb-bot 1.31+)" + , File.hasPrivContent "/etc/kgb-bot/kgb.conf" anyContext + `onChange` Service.restarted "kgb-bot" + ] where desc = "kgb.kitenet.net setup" mumbleServer :: [Host] -> Property -mumbleServer hosts = combineProperties "mumble.debian.net" +mumbleServer hosts = combineProperties hn [ Apt.serviceInstalledRunning "mumble-server" , Obnam.latestVersion , Obnam.backup "/var/lib/mumble-server" "55 5 * * *" - [ "--repository=sftp://joey@turtle.kitenet.net/~/lib/backup/mumble.debian.net.obnam" + [ "--repository=sftp://joey@turtle.kitenet.net/~/lib/backup/" ++ hn ++ ".obnam" , "--client-name=mumble" ] Obnam.OnlyClient - `requires` Ssh.keyImported SshRsa "root" + `requires` Ssh.keyImported SshRsa "root" (Context hn) `requires` Ssh.knownHost hosts "turtle.kitenet.net" "root" , trivial $ cmdProperty "chown" ["-R", "mumble-server:mumble-server", "/var/lib/mumble-server"] ] + where + hn = "mumble.debian.net" obnamLowMem :: Property obnamLowMem = combineProperties "obnam tuned for low memory use" @@ -137,16 +145,16 @@ gitServer hosts = propertyList "git.kitenet.net setup" , "--client-name=wren" ] Obnam.OnlyClient `requires` Gpg.keyImported "1B169BE1" "root" - `requires` Ssh.keyImported SshRsa "root" + `requires` Ssh.keyImported SshRsa "root" (Context "git.kitenet.net") `requires` Ssh.knownHost hosts "usw-s002.rsync.net" "root" - `requires` Ssh.authorizedKeys "family" + `requires` Ssh.authorizedKeys "family" (Context "git.kitenet.net") `requires` User.accountFor "family" , Apt.installed ["git", "rsync", "gitweb"] -- backport avoids channel flooding on branch merge , Apt.installedBackport ["kgb-client"] -- backport supports ssh event notification , Apt.installedBackport ["git-annex"] - , File.hasPrivContentExposed "/etc/kgb-bot/kgb-client.conf" + , File.hasPrivContentExposed "/etc/kgb-bot/kgb-client.conf" anyContext , toProp $ Git.daemonRunning "/srv/git" , "/etc/gitweb.conf" `File.containsLines` [ "$projectroot = '/srv/git';" @@ -198,7 +206,7 @@ annexWebSite hosts origin hn uuid remotes = propertyList (hn ++" website using g dir = "/srv/web/" ++ hn postupdatehook = dir ".git/hooks/post-update" setup = userScriptProperty "joey" setupscript - `requires` Ssh.keyImported SshRsa "joey" + `requires` Ssh.keyImported SshRsa "joey" (Context hn) `requires` Ssh.knownHost hosts "turtle.kitenet.net" "joey" setupscript = [ "cd " ++ shellEscape dir @@ -266,9 +274,9 @@ mainhttpscert True = gitAnnexDistributor :: Property gitAnnexDistributor = combineProperties "git-annex distributor, including rsync server and signer" [ Apt.installed ["rsync"] - , File.hasPrivContent "/etc/rsyncd.conf" + , File.hasPrivContent "/etc/rsyncd.conf" (Context "git-annex distributor") `onChange` Service.restarted "rsync" - , File.hasPrivContent "/etc/rsyncd.secrets" + , File.hasPrivContent "/etc/rsyncd.secrets" (Context "git-annex distributor") `onChange` Service.restarted "rsync" , "/etc/default/rsync" `File.containsLine` "RSYNC_ENABLE=true" `onChange` Service.running "rsync" @@ -310,10 +318,13 @@ ircBouncer :: Property ircBouncer = propertyList "IRC bouncer" [ Apt.installed ["znc"] , User.accountFor "znc" - , File.hasPrivContent conf + , File.dirExists (parentDir conf) + , File.hasPrivContent conf anyContext , File.ownerGroup conf "znc" "znc" , Cron.job "znconboot" "@reboot" "znc" "~" "znc" - , Cron.job "zncrunning" "@hourly" "znc" "~" "znc || true" + -- ensure running if it was not already + , trivial $ userScriptProperty "znc" ["znc || true"] + `describe` "znc running" ] where conf = "/home/znc/.znc/configs/znc.conf" @@ -335,7 +346,7 @@ githubBackup :: Property githubBackup = propertyList "github-backup box" [ Apt.installed ["github-backup", "moreutils"] , let f = "/home/joey/.github-keys" - in File.hasPrivContent f + in File.hasPrivContent f anyContext `onChange` File.ownerGroup f "joey" "joey" ] diff --git a/src/Propellor/Property/Ssh.hs b/src/Propellor/Property/Ssh.hs index 061f440..5a26047 100644 --- a/src/Propellor/Property/Ssh.hs +++ b/src/Propellor/Property/Ssh.hs @@ -5,6 +5,7 @@ module Propellor.Property.Ssh ( hasAuthorizedKeys, restartSshd, randomHostKeys, + hostKeys, hostKey, keyImported, knownHost, @@ -72,46 +73,54 @@ randomHostKeys = flagFile prop "/etc/ssh/.unique_host_keys" [ Param "-c" , Param "rm -f /etc/ssh/ssh_host_*" ] - ensureProperty $ - cmdProperty "/var/lib/dpkg/info/openssh-server.postinst" - ["configure"] + ensureProperty $ scriptProperty + [ "DPKG_MAINTSCRIPT_NAME=postinst DPKG_MAINTSCRIPT_PACKAGE=openssh-server /var/lib/dpkg/info/openssh-server.postinst configure" ] --- | Sets ssh host keys from the site's PrivData. --- --- (Uses a null username for host keys.) -hostKey :: SshKeyType -> Property -hostKey keytype = combineProperties desc - [ property desc (install writeFile (SshPubKey keytype "") ".pub") - , property desc (install writeFileProtected (SshPrivKey keytype "") "") +-- | Sets all types of ssh host keys from the privdata. +hostKeys :: Context -> Property +hostKeys ctx = propertyList "known ssh host keys" + [ hostKey SshDsa ctx + , hostKey SshRsa ctx + , hostKey SshEcdsa ctx + ] + +-- | Sets a single ssh host key from the privdata. +hostKey :: SshKeyType -> Context -> Property +hostKey keytype context = combineProperties desc + [ installkey (SshPubKey keytype "") (install writeFile ".pub") + , installkey (SshPrivKey keytype "") (install writeFileProtected "") ] `onChange` restartSshd where desc = "known ssh host key (" ++ fromKeyType keytype ++ ")" - install writer p ext = withPrivData p $ \key -> do + installkey p a = withPrivData p context $ \getkey -> + property desc $ getkey a + install writer ext key = do let f = "/etc/ssh/ssh_host_" ++ fromKeyType keytype ++ "_key" ++ ext s <- liftIO $ readFileStrict f if s == key then noChange else makeChange $ writer f key --- | Sets up a user with a ssh private key and public key pair --- from the site's PrivData. -keyImported :: SshKeyType -> UserName -> Property -keyImported keytype user = combineProperties desc - [ property desc (install writeFile (SshPubKey keytype user) ".pub") - , property desc (install writeFileProtected (SshPrivKey keytype user) "") +-- | Sets up a user with a ssh private key and public key pair from the +-- PrivData. +keyImported :: SshKeyType -> UserName -> Context -> Property +keyImported keytype user context = combineProperties desc + [ installkey (SshPubKey keytype user) (install writeFile ".pub") + , installkey (SshPrivKey keytype user) (install writeFileProtected "") ] where desc = user ++ " has ssh key (" ++ fromKeyType keytype ++ ")" - install writer p ext = do + installkey p a = withPrivData p context $ \getkey -> + property desc $ getkey a + install writer ext key = do f <- liftIO $ keyfile ext ifM (liftIO $ doesFileExist f) ( noChange , ensureProperties - [ property desc $ - withPrivData p $ \key -> makeChange $ do - createDirectoryIfMissing True (takeDirectory f) - writer f key + [ property desc $ makeChange $ do + createDirectoryIfMissing True (takeDirectory f) + writer f key , File.ownerGroup f user user , File.ownerGroup (takeDirectory f) user user ] @@ -144,9 +153,9 @@ knownHost hosts hn user = property desc $ return FailedChange -- | Makes a user have authorized_keys from the PrivData -authorizedKeys :: UserName -> Property -authorizedKeys user = property (user ++ " has authorized_keys") $ - withPrivData (SshAuthorizedKeys user) $ \v -> do +authorizedKeys :: UserName -> Context -> Property +authorizedKeys user context = withPrivData (SshAuthorizedKeys user) context $ \get -> + property (user ++ " has authorized_keys") $ get $ \v -> do f <- liftIO $ dotFile "authorized_keys" user liftIO $ do createDirectoryIfMissing True (takeDirectory f) diff --git a/src/Propellor/Property/User.hs b/src/Propellor/Property/User.hs index eef2a57..f9c400a 100644 --- a/src/Propellor/Property/User.hs +++ b/src/Propellor/Property/User.hs @@ -24,17 +24,18 @@ nuked user _ = check (isJust <$> catchMaybeIO (homedir user)) $ cmdProperty "use -- | Only ensures that the user has some password set. It may or may -- not be the password from the PrivData. -hasSomePassword :: UserName -> Property -hasSomePassword user = check ((/= HasPassword) <$> getPasswordStatus user) $ - hasPassword user +hasSomePassword :: UserName -> Context -> Property +hasSomePassword user context = check ((/= HasPassword) <$> getPasswordStatus user) $ + hasPassword user context -hasPassword :: UserName -> Property -hasPassword user = property (user ++ " has password") $ - withPrivData (Password user) $ \password -> makeChange $ - withHandle StdinHandle createProcessSuccess - (proc "chpasswd" []) $ \h -> do - hPutStrLn h $ user ++ ":" ++ password - hClose h +hasPassword :: UserName -> Context -> Property +hasPassword user context = withPrivData (Password user) context $ \getpassword -> + property (user ++ " has password") $ + getpassword $ \password -> makeChange $ + withHandle StdinHandle createProcessSuccess + (proc "chpasswd" []) $ \h -> do + hPutStrLn h $ user ++ ":" ++ password + hClose h lockedPassword :: UserName -> Property lockedPassword user = check (not <$> isLockedPassword user) $ cmdProperty "passwd" diff --git a/src/Propellor/Types.hs b/src/Propellor/Types.hs index 383797a..037cd96 100644 --- a/src/Propellor/Types.hs +++ b/src/Propellor/Types.hs @@ -17,7 +17,9 @@ module Propellor.Types , ActionResult(..) , CmdLine(..) , PrivDataField(..) - , GpgKeyId + , PrivData + , Context(..) + , anyContext , SshKeyType(..) , module Propellor.Types.OS , module Propellor.Types.Dns @@ -32,6 +34,7 @@ import "MonadCatchIO-transformers" Control.Monad.CatchIO import Propellor.Types.Info import Propellor.Types.OS import Propellor.Types.Dns +import Propellor.Types.PrivData -- | Everything Propellor knows about a system: Its hostname, -- properties and other info. @@ -135,28 +138,12 @@ data CmdLine = Run HostName | Spin HostName | Boot HostName - | Set HostName PrivDataField - | Dump HostName PrivDataField + | Set PrivDataField Context + | Dump PrivDataField Context + | Edit PrivDataField Context + | ListFields | AddKey String | Continue CmdLine | Chain HostName | Docker HostName deriving (Read, Show, Eq) - --- | Note that removing or changing field names will break the --- serialized privdata files, so don't do that! --- It's fine to add new fields. -data PrivDataField - = DockerAuthentication - | SshPubKey SshKeyType UserName - | SshPrivKey SshKeyType UserName - | SshAuthorizedKeys UserName - | Password UserName - | PrivFile FilePath - | GpgKey GpgKeyId - deriving (Read, Show, Ord, Eq) - -type GpgKeyId = String - -data SshKeyType = SshRsa | SshDsa | SshEcdsa | SshEd25519 - deriving (Read, Show, Ord, Eq) diff --git a/src/Propellor/Types/Info.hs b/src/Propellor/Types/Info.hs index 5f03449..8856e06 100644 --- a/src/Propellor/Types/Info.hs +++ b/src/Propellor/Types/Info.hs @@ -1,6 +1,7 @@ module Propellor.Types.Info where import Propellor.Types.OS +import Propellor.Types.PrivData import qualified Propellor.Types.Dns as Dns import qualified Data.Set as S @@ -9,6 +10,7 @@ import Data.Monoid -- | Information about a host. data Info = Info { _os :: Val System + , _privDataFields :: S.Set (PrivDataField, Context) , _sshPubKey :: Val String , _dns :: S.Set Dns.Record , _namedconf :: Dns.NamedConfMap @@ -17,9 +19,10 @@ data Info = Info deriving (Eq, Show) instance Monoid Info where - mempty = Info mempty mempty mempty mempty mempty + mempty = Info mempty mempty mempty mempty mempty mempty mappend old new = Info { _os = _os old <> _os new + , _privDataFields = _privDataFields old <> _privDataFields new , _sshPubKey = _sshPubKey old <> _sshPubKey new , _dns = _dns old <> _dns new , _namedconf = _namedconf old <> _namedconf new diff --git a/src/Propellor/Types/PrivData.hs b/src/Propellor/Types/PrivData.hs new file mode 100644 index 0000000..16d6cdb --- /dev/null +++ b/src/Propellor/Types/PrivData.hs @@ -0,0 +1,34 @@ +module Propellor.Types.PrivData where + +import Propellor.Types.OS + +-- | Note that removing or changing field names will break the +-- serialized privdata files, so don't do that! +-- It's fine to add new fields. +data PrivDataField + = DockerAuthentication + | SshPubKey SshKeyType UserName + | SshPrivKey SshKeyType UserName + | SshAuthorizedKeys UserName + | Password UserName + | PrivFile FilePath + | GpgKey + deriving (Read, Show, Ord, Eq) + +-- | Context in which a PrivDataField is used. +-- +-- Often this will be a domain name. For example, +-- Context "www.example.com" could be used for the SSL cert +-- for the web server serving that domain. Multiple hosts might +-- use that privdata. +newtype Context = Context String + deriving (Read, Show, Ord, Eq) + +-- | Use when a PrivDataField is not dependent on any paricular context. +anyContext :: Context +anyContext = Context "any" + +type PrivData = String + +data SshKeyType = SshRsa | SshDsa | SshEcdsa | SshEd25519 + deriving (Read, Show, Ord, Eq) diff --git a/src/Utility/Table.hs b/src/Utility/Table.hs new file mode 100644 index 0000000..910038e --- /dev/null +++ b/src/Utility/Table.hs @@ -0,0 +1,28 @@ +{- text based table generation + - + - Copyright 2014 Joey Hess + - + - License: BSD-2-clause + -} + +module Utility.Table where + +type Table = [[String]] + +-- | A table with a header that is set off with lines under each +-- header item. +tableWithHeader :: [String] -> [[String]] -> Table +tableWithHeader header rows = header : map linesep header : rows + where + linesep = map (const '-') + +-- | Formats a table to lines, automatically padding rows to the same size. +formatTable :: Table -> [String] +formatTable table = map (\r -> unwords (map pad (zip r rowsizes))) table + where + pad (cell, size) = cell ++ take (size - length cell) padding + padding = repeat ' ' + rowsizes = sumrows (map (map length) table) + sumrows [] = repeat 0 + sumrows [r] = r + sumrows (r1:r2:rs) = sumrows $ map (uncurry max) (zip r1 r2) : rs