module Propellor.Property.DnsSec where
import Propellor.Base
import qualified Propellor.Property.File as File
keysInstalled :: Domain -> RevertableProperty (HasInfo + UnixLike) UnixLike
keysInstalled domain = setup <!> cleanup
where
setup = propertyList "DNSSEC keys installed" $ toProps $
map installkey keys
cleanup = propertyList "DNSSEC keys removed" $ toProps $
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
]
zoneSigned :: Domain -> FilePath -> RevertableProperty (HasInfo + UnixLike) UnixLike
zoneSigned domain zonefile = setup <!> cleanup
where
setup :: Property (HasInfo + UnixLike)
setup = check needupdate (forceZoneSigned domain zonefile)
`requires` keysInstalled domain
cleanup :: Property UnixLike
cleanup = File.notPresent (signedZoneFile zonefile)
`before` File.notPresent dssetfile
`before` revert (keysInstalled domain)
dssetfile = dir </> "-" ++ domain ++ "."
dir = takeDirectory zonefile
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 UnixLike
forceZoneSigned domain zonefile = property ("zone signed for " ++ domain) $ liftIO $ do
salt <- take 16 <$> saltSha1
let p = proc "dnssec-signzone"
[ "-A"
, "-3", salt
, "-N", "unixtime"
, "-o", domain
, zonefile
, keyFn domain PubZSK
, keyFn domain PubKSK
]
(_, _, _, 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"
]
keyFn :: Domain -> DnsSecKey -> FilePath
keyFn domain k = "/etc/bind/propellor/dnssec" </> concat
[ "K" ++ domain ++ "."
, if isZoneSigningKey k then "ZSK" else "KSK"
, keyExt k
]
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]
signedZoneFile :: FilePath -> FilePath
signedZoneFile zonefile = zonefile ++ ".signed"