summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Hess2014-11-11 13:47:25 -0400
committerJoey Hess2014-11-11 13:47:25 -0400
commit08ff95fbfaf867ad5a6acdecfd0eb1e84ed44fd9 (patch)
treeb5fa72c40d3c6c1438e7ab3191c1136887fe7b4f
parentbd7869c01c38065275acfdc4b139a93439433229 (diff)
parent05a793dd5916a3d21cbec783e26bd629891ad7f1 (diff)
Merge branch 'joeyconfig'
-rw-r--r--debian/changelog2
-rw-r--r--doc/todo/multi_gpg_key_privdata.mdwn10
-rw-r--r--propellor.cabal2
-rw-r--r--src/Propellor/CmdLine.hs44
-rw-r--r--src/Propellor/Gpg.hs115
-rw-r--r--src/Propellor/PrivData.hs31
-rw-r--r--src/Propellor/PrivData/Paths.hs12
7 files changed, 144 insertions, 72 deletions
diff --git a/debian/changelog b/debian/changelog
index c87ec6e1..3fef3404 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,8 @@ propellor (0.9.3) UNRELEASED; urgency=medium
* Added prosody module, contributed by Félix Sipma.
* Can be used to configure tor hidden services. Thanks, Félix Sipma.
+ * When multiple gpg keys are added, ensure that the privdata file
+ can be decrypted by all of them.
-- Joey Hess <joeyh@debian.org> Mon, 10 Nov 2014 11:15:27 -0400
diff --git a/doc/todo/multi_gpg_key_privdata.mdwn b/doc/todo/multi_gpg_key_privdata.mdwn
index 754aa7e9..3ee6b3b8 100644
--- a/doc/todo/multi_gpg_key_privdata.mdwn
+++ b/doc/todo/multi_gpg_key_privdata.mdwn
@@ -1,6 +1,14 @@
To support multiple gpg keys added with --add-key, propellor should
* When it encrypts the privdata after a change, encrypt it to all keys
- listed in `privdata/keyring.gpg`
+ listed in `privdata/keyring.gpg`. See [this
+ post](http://laurent.bachelier.name/2013/03/gpg-encryption-to-multiple-recipients/)
+ explaining why and how encryption with multiple recipients work.
* When --add-key adds a new key, it should re-encrypt the privdata,
so that this new key can access it.
+* When --add-key on behalf of another user, do not modify the signing key for
+ local git. This entails either splitting this command in two, `--add-key` and
+ `--set-signing-key`, or adding another command `--add-foreign-key`,
+ or perhaps determining if the key being added has a known secret key.
+
+[[done]]
diff --git a/propellor.cabal b/propellor.cabal
index 5ec657a9..4da14192 100644
--- a/propellor.cabal
+++ b/propellor.cabal
@@ -113,7 +113,9 @@ Library
Other-Modules:
Propellor.Types.Info
Propellor.CmdLine
+ Propellor.Gpg
Propellor.SimpleSh
+ Propellor.PrivData.Paths
Propellor.Property.Docker.Shim
Utility.Applicative
Utility.Data
diff --git a/src/Propellor/CmdLine.hs b/src/Propellor/CmdLine.hs
index 415b8576..c3b792d1 100644
--- a/src/Propellor/CmdLine.hs
+++ b/src/Propellor/CmdLine.hs
@@ -13,6 +13,8 @@ import System.Posix.IO
import Data.Time.Clock.POSIX
import Propellor
+import Propellor.PrivData.Paths
+import Propellor.Gpg
import qualified Propellor.Property.Docker as Docker
import qualified Propellor.Property.Docker.Shim as DockerShim
import Utility.FileMode
@@ -303,48 +305,6 @@ boot h = do
fromMarked privDataMarker reply
mainProperties h
-addKey :: String -> IO ()
-addKey keyid = exitBool =<< allM id [ gpg, gitadd, gitconfig, gitcommit ]
- where
- gpg = do
- createDirectoryIfMissing True privDataDir
- boolSystem "sh"
- [ Param "-c"
- , Param $ "gpg --export " ++ keyid ++ " | gpg " ++
- unwords (gpgopts ++ ["--import"])
- ]
- gitadd = boolSystem "git"
- [ Param "add"
- , File keyring
- ]
-
- gitconfig = boolSystem "git"
- [ Param "config"
- , Param "user.signingkey"
- , Param keyid
- ]
-
- gitcommit = gitCommit
- [ File keyring
- , Param "-m"
- , Param "propellor addkey"
- ]
-
-{- Automatically sign the commit if there'a a keyring. -}
-gitCommit :: [CommandParam] -> IO Bool
-gitCommit ps = do
- k <- doesFileExist keyring
- boolSystem "git" $ catMaybes $
- [ Just (Param "commit")
- , if k then Just (Param "--gpg-sign") else Nothing
- ] ++ map Just ps
-
-keyring :: FilePath
-keyring = privDataDir </> "keyring.gpg"
-
-gpgopts :: [String]
-gpgopts = ["--options", "/dev/null", "--no-default-keyring", "--keyring", keyring]
-
getUrl :: IO String
getUrl = maybe nourl return =<< getM get urls
where
diff --git a/src/Propellor/Gpg.hs b/src/Propellor/Gpg.hs
new file mode 100644
index 00000000..572be190
--- /dev/null
+++ b/src/Propellor/Gpg.hs
@@ -0,0 +1,115 @@
+module Propellor.Gpg where
+
+import Control.Applicative
+import System.IO
+import System.FilePath
+import System.Directory
+import Data.Maybe
+import Data.List.Utils
+
+import Propellor.PrivData.Paths
+import Propellor.Message
+import Utility.SafeCommand
+import Utility.Process
+import Utility.Monad
+import Utility.Misc
+import Utility.Tmp
+
+type KeyId = String
+
+keyring :: FilePath
+keyring = privDataDir </> "keyring.gpg"
+
+-- Lists the keys in propellor's keyring.
+listPubKeys :: IO [KeyId]
+listPubKeys = parse . lines <$> readProcess "gpg" listopts
+ where
+ listopts = useKeyringOpts ++ ["--with-colons", "--list-public-keys"]
+ parse = mapMaybe (keyIdField . split ":")
+ keyIdField ("pub":_:_:_:f:_) = Just f
+ keyIdField _ = Nothing
+
+useKeyringOpts :: [String]
+useKeyringOpts =
+ [ "--options"
+ , "/dev/null"
+ , "--no-default-keyring"
+ , "--keyring", keyring
+ ]
+
+addKey :: KeyId -> IO ()
+addKey keyid = exitBool =<< allM (uncurry actionMessage)
+ [ ("adding key to propellor's keyring", addkeyring)
+ , ("staging propellor's keyring", gitadd keyring)
+ , ("updating encryption of any privdata", reencryptprivdata)
+ , ("configuring git signing to use key", gitconfig)
+ , ("committing changes", gitcommit)
+ ]
+ where
+ addkeyring = do
+ createDirectoryIfMissing True privDataDir
+ boolSystem "sh"
+ [ Param "-c"
+ , Param $ "gpg --export " ++ keyid ++ " | gpg " ++
+ unwords (useKeyringOpts ++ ["--import"])
+ ]
+
+ reencryptprivdata = ifM (doesFileExist privDataFile)
+ ( do
+ gpgEncrypt privDataFile =<< gpgDecrypt privDataFile
+ gitadd privDataFile
+ , return True
+ )
+
+ gitadd f = boolSystem "git"
+ [ Param "add"
+ , File f
+ ]
+
+ gitconfig = ifM (snd <$> processTranscript "gpg" ["--list-secret-keys", keyid] Nothing)
+ ( boolSystem "git"
+ [ Param "config"
+ , Param "user.signingkey"
+ , Param keyid
+ ]
+ , do
+ warningMessage $ "Cannot find a secret key for key " ++ keyid ++ ", so not configuring git user.signingkey to use this key."
+ return True
+ )
+
+ gitcommit = gitCommit
+ [ File keyring
+ , Param "-m"
+ , Param "propellor addkey"
+ ]
+
+-- Automatically sign the commit if there'a a keyring.
+gitCommit :: [CommandParam] -> IO Bool
+gitCommit ps = do
+ k <- doesFileExist keyring
+ boolSystem "git" $ catMaybes $
+ [ Just (Param "commit")
+ , if k then Just (Param "--gpg-sign") else Nothing
+ ] ++ map Just ps
+
+gpgDecrypt :: FilePath -> IO String
+gpgDecrypt f = ifM (doesFileExist f)
+ ( readProcess "gpg" ["--decrypt", f]
+ , return ""
+ )
+
+-- Encrypt file to all keys in propellor's keyring.
+gpgEncrypt :: FilePath -> String -> IO ()
+gpgEncrypt f s = do
+ keyids <- listPubKeys
+ let opts =
+ [ "--default-recipient-self"
+ , "--armor"
+ , "--encrypt"
+ , "--trust-model", "always"
+ ] ++ concatMap (\k -> ["--recipient", k]) keyids
+ encrypted <- writeReadProcessEnv "gpg" opts
+ Nothing
+ (Just $ flip hPutStr s)
+ Nothing
+ viaTmp writeFile f encrypted
diff --git a/src/Propellor/PrivData.hs b/src/Propellor/PrivData.hs
index f55ab74c..a5150432 100644
--- a/src/Propellor/PrivData.hs
+++ b/src/Propellor/PrivData.hs
@@ -3,7 +3,6 @@
module Propellor.PrivData where
import Control.Applicative
-import System.FilePath
import System.IO
import System.Directory
import Data.Maybe
@@ -19,10 +18,11 @@ import Propellor.Types
import Propellor.Types.Info
import Propellor.Message
import Propellor.Info
+import Propellor.Gpg
+import Propellor.PrivData.Paths
import Utility.Monad
import Utility.PartialPrelude
import Utility.Exception
-import Utility.Process
import Utility.Tmp
import Utility.SafeCommand
import Utility.Misc
@@ -146,30 +146,3 @@ decryptPrivData = fromMaybe M.empty . readish <$> gpgDecrypt privDataFile
makePrivDataDir :: IO ()
makePrivDataDir = createDirectoryIfMissing False privDataDir
-
-privDataDir :: FilePath
-privDataDir = "privdata"
-
-privDataFile :: FilePath
-privDataFile = privDataDir </> "privdata.gpg"
-
-privDataLocal :: FilePath
-privDataLocal = privDataDir </> "local"
-
-gpgDecrypt :: FilePath -> IO String
-gpgDecrypt f = ifM (doesFileExist f)
- ( readProcess "gpg" ["--decrypt", f]
- , return ""
- )
-
-gpgEncrypt :: FilePath -> String -> IO ()
-gpgEncrypt f s = do
- encrypted <- writeReadProcessEnv "gpg"
- [ "--default-recipient-self"
- , "--armor"
- , "--encrypt"
- ]
- Nothing
- (Just $ flip hPutStr s)
- Nothing
- viaTmp writeFile f encrypted
diff --git a/src/Propellor/PrivData/Paths.hs b/src/Propellor/PrivData/Paths.hs
new file mode 100644
index 00000000..7c29f1bf
--- /dev/null
+++ b/src/Propellor/PrivData/Paths.hs
@@ -0,0 +1,12 @@
+module Propellor.PrivData.Paths where
+
+import System.FilePath
+
+privDataDir :: FilePath
+privDataDir = "privdata"
+
+privDataFile :: FilePath
+privDataFile = privDataDir </> "privdata.gpg"
+
+privDataLocal :: FilePath
+privDataLocal = privDataDir </> "local"