summaryrefslogtreecommitdiff
path: root/src/Propellor/Property/DnsSec.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Propellor/Property/DnsSec.hs')
-rw-r--r--src/Propellor/Property/DnsSec.hs122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/Propellor/Property/DnsSec.hs b/src/Propellor/Property/DnsSec.hs
new file mode 100644
index 00000000..b7557006
--- /dev/null
+++ b/src/Propellor/Property/DnsSec.hs
@@ -0,0 +1,122 @@
+module Propellor.Property.DnsSec where
+
+import Propellor
+import qualified Propellor.Property.File as File
+
+-- | Puts the DNSSEC key files in place from PrivData.
+--
+-- signedPrimary uses this, so this property does not normally need to be
+-- used directly.
+keysInstalled :: Domain -> RevertableProperty
+keysInstalled domain = RevertableProperty setup cleanup
+ where
+ setup = propertyList "DNSSEC keys installed" $
+ map installkey keys
+
+ cleanup = propertyList "DNSSEC keys removed" $
+ map (File.notPresent . keyFn domain) keys
+
+ installkey k = writer (keysrc k) (keyFn domain k) (Context domain)
+ where
+ writer
+ | isPublic k = File.hasPrivContentExposedFrom
+ | otherwise = File.hasPrivContentFrom
+
+ keys = [ PubZSK, PrivZSK, PubKSK, PrivKSK ]
+
+ keysrc k = PrivDataSource (DnsSec k) $ unwords
+ [ "The file with extension"
+ , keyExt k
+ , "created by running:"
+ , if isZoneSigningKey k
+ then "dnssec-keygen -a RSASHA256 -b 2048 -n ZONE " ++ domain
+ else "dnssec-keygen -f KSK -a RSASHA256 -b 4096 -n ZONE " ++ domain
+ ]
+
+-- | Uses dnssec-signzone to sign a domain's zone file.
+--
+-- signedPrimary uses this, so this property does not normally need to be
+-- used directly.
+zoneSigned :: Domain -> FilePath -> RevertableProperty
+zoneSigned domain zonefile = RevertableProperty setup cleanup
+ where
+ setup = check needupdate (forceZoneSigned domain zonefile)
+ `requires` toProp (keysInstalled domain)
+
+ cleanup = combineProperties ("removed signed zone for " ++ domain)
+ [ File.notPresent (signedZoneFile zonefile)
+ , File.notPresent dssetfile
+ , toProp (revert (keysInstalled domain))
+ ]
+
+ dssetfile = dir </> "-" ++ domain ++ "."
+ dir = takeDirectory zonefile
+
+ -- Need to update the signed zone file if the zone file or
+ -- any of the keys have a newer timestamp.
+ needupdate = do
+ v <- catchMaybeIO $ getModificationTime (signedZoneFile zonefile)
+ case v of
+ Nothing -> return True
+ Just t1 -> anyM (newerthan t1) $
+ zonefile : map (keyFn domain) [minBound..maxBound]
+
+ newerthan t1 f = do
+ t2 <- getModificationTime f
+ return (t2 >= t1)
+
+forceZoneSigned :: Domain -> FilePath -> Property
+forceZoneSigned domain zonefile = property ("zone signed for " ++ domain) $ liftIO $ do
+ salt <- take 16 <$> saltSha1
+ let p = proc "dnssec-signzone"
+ [ "-A"
+ , "-3", salt
+ -- The serial number needs to be increased each time the
+ -- zone is resigned, even if there are no other changes,
+ -- so that it will propigate to secondaries. So, use the
+ -- unixtime serial format.
+ , "-N", "unixtime"
+ , "-o", domain
+ , zonefile
+ -- the ordering of these key files does not matter
+ , keyFn domain PubZSK
+ , keyFn domain PubKSK
+ ]
+ -- Run in the same directory as the zonefile, so it will
+ -- write the dsset file there.
+ (_, _, _, h) <- createProcess $
+ p { cwd = Just (takeDirectory zonefile) }
+ ifM (checkSuccessProcess h)
+ ( return MadeChange
+ , return FailedChange
+ )
+
+saltSha1 :: IO String
+saltSha1 = readProcess "sh"
+ [ "-c"
+ , "head -c 1024 /dev/urandom | sha1sum | cut -d ' ' -f 1"
+ ]
+
+-- | The file used for a given key.
+keyFn :: Domain -> DnsSecKey -> FilePath
+keyFn domain k = "/etc/bind/propellor/dnssec" </> concat
+ [ "K" ++ domain ++ "."
+ , if isZoneSigningKey k then "ZSK" else "KSK"
+ , keyExt k
+ ]
+
+-- | These are the extensions that dnssec-keygen looks for.
+keyExt :: DnsSecKey -> String
+keyExt k
+ | isPublic k = ".key"
+ | otherwise = ".private"
+
+isPublic :: DnsSecKey -> Bool
+isPublic k = k `elem` [PubZSK, PubKSK]
+
+isZoneSigningKey :: DnsSecKey -> Bool
+isZoneSigningKey k = k `elem` [PubZSK, PrivZSK]
+
+-- | dnssec-signzone makes a .signed file
+signedZoneFile :: FilePath -> FilePath
+signedZoneFile zonefile = zonefile ++ ".signed"