diff options
Diffstat (limited to 'src/Propellor/Property/DnsSec.hs')
| -rw-r--r-- | src/Propellor/Property/DnsSec.hs | 122 |
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" |
