add dns records to Attr
This commit is contained in:
parent
2b9ee5b29b
commit
39d697ca78
|
@ -4,6 +4,7 @@ module Propellor.Attr where
|
||||||
|
|
||||||
import Propellor.Types
|
import Propellor.Types
|
||||||
import Propellor.Types.Attr
|
import Propellor.Types.Attr
|
||||||
|
import Propellor.Types.Dns
|
||||||
|
|
||||||
import "mtl" Control.Monad.Reader
|
import "mtl" Control.Monad.Reader
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
@ -28,15 +29,16 @@ getOS :: Propellor (Maybe System)
|
||||||
getOS = asks _os
|
getOS = asks _os
|
||||||
|
|
||||||
cname :: Domain -> Property
|
cname :: Domain -> Property
|
||||||
cname domain = pureAttrProperty ("cname " ++ domain) (addCName domain)
|
cname domain = pureAttrProperty ("cname " ++ domain)
|
||||||
|
(addDNS $ CNAME $ AbsDomain domain)
|
||||||
|
|
||||||
cnameFor :: Domain -> (Domain -> Property) -> Property
|
cnameFor :: Domain -> (Domain -> Property) -> Property
|
||||||
cnameFor domain mkp =
|
cnameFor domain mkp =
|
||||||
let p = mkp domain
|
let p = mkp domain
|
||||||
in p { propertyAttr = propertyAttr p . addCName domain }
|
in p { propertyAttr = propertyAttr p . addDNS (CNAME $ AbsDomain domain) }
|
||||||
|
|
||||||
addCName :: HostName -> SetAttr
|
addDNS :: Record -> SetAttr
|
||||||
addCName domain d = d { _cnames = S.insert domain (_cnames d) }
|
addDNS record d = d { _dns = S.insert record (_dns d) }
|
||||||
|
|
||||||
sshPubKey :: String -> Property
|
sshPubKey :: String -> Property
|
||||||
sshPubKey k = pureAttrProperty ("ssh pubkey known") $
|
sshPubKey k = pureAttrProperty ("ssh pubkey known") $
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
module Propellor.Property.Dns where
|
module Propellor.Property.Dns (
|
||||||
|
module Propellor.Types.Dns,
|
||||||
|
secondary,
|
||||||
|
servingZones,
|
||||||
|
mkSOA,
|
||||||
|
nextSerialNumber,
|
||||||
|
incrSerialNumber,
|
||||||
|
currentSerialNumber,
|
||||||
|
writeZoneFile,
|
||||||
|
genZoneFile,
|
||||||
|
genSOA,
|
||||||
|
) where
|
||||||
|
|
||||||
import Propellor
|
import Propellor
|
||||||
|
import Propellor.Types.Dns
|
||||||
import Propellor.Property.File
|
import Propellor.Property.File
|
||||||
import qualified Propellor.Property.Apt as Apt
|
import qualified Propellor.Property.Apt as Apt
|
||||||
import qualified Propellor.Property.Service as Service
|
import qualified Propellor.Property.Service as Service
|
||||||
|
@ -8,48 +20,31 @@ import Utility.Applicative
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Time.Clock.POSIX
|
import Data.Time.Clock.POSIX
|
||||||
import Data.Time.Format
|
|
||||||
import Foreign.C.Types
|
|
||||||
|
|
||||||
namedconf :: FilePath
|
namedconf :: FilePath
|
||||||
namedconf = "/etc/bind/named.conf.local"
|
namedconf = "/etc/bind/named.conf.local"
|
||||||
|
|
||||||
data NamedConf = NamedConf
|
|
||||||
{ zdomain :: Domain
|
|
||||||
, ztype :: Type
|
|
||||||
, zfile :: FilePath
|
|
||||||
, zmasters :: [IPAddr]
|
|
||||||
, zconfiglines :: [String]
|
|
||||||
}
|
|
||||||
|
|
||||||
zoneDesc :: NamedConf -> String
|
zoneDesc :: NamedConf -> String
|
||||||
zoneDesc z = zdomain z ++ " (" ++ show (ztype z) ++ ")"
|
zoneDesc z = confDomain z ++ " (" ++ show (confType z) ++ ")"
|
||||||
|
|
||||||
type IPAddr = String
|
|
||||||
|
|
||||||
type Domain = String
|
|
||||||
|
|
||||||
data Type = Master | Secondary
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
secondary :: Domain -> [IPAddr] -> NamedConf
|
secondary :: Domain -> [IPAddr] -> NamedConf
|
||||||
secondary domain masters = NamedConf
|
secondary domain masters = NamedConf
|
||||||
{ zdomain = domain
|
{ confDomain = domain
|
||||||
, ztype = Secondary
|
, confType = Secondary
|
||||||
, zfile = "db." ++ domain
|
, confFile = "db." ++ domain
|
||||||
, zmasters = masters
|
, confMasters = masters
|
||||||
, zconfiglines = ["allow-transfer { }"]
|
, confLines = ["allow-transfer { }"]
|
||||||
}
|
}
|
||||||
|
|
||||||
zoneStanza :: NamedConf -> [Line]
|
confStanza :: NamedConf -> [Line]
|
||||||
zoneStanza z =
|
confStanza c =
|
||||||
[ "// automatically generated by propellor"
|
[ "// automatically generated by propellor"
|
||||||
, "zone \"" ++ zdomain z ++ "\" {"
|
, "zone \"" ++ confDomain c ++ "\" {"
|
||||||
, cfgline "type" (if ztype z == Master then "master" else "slave")
|
, cfgline "type" (if confType c == Master then "master" else "slave")
|
||||||
, cfgline "file" ("\"" ++ zfile z ++ "\"")
|
, cfgline "file" ("\"" ++ confFile c ++ "\"")
|
||||||
] ++
|
] ++
|
||||||
(if null (zmasters z) then [] else mastersblock) ++
|
(if null (confMasters c) then [] else mastersblock) ++
|
||||||
(map (\l -> "\t" ++ l ++ ";") (zconfiglines z)) ++
|
(map (\l -> "\t" ++ l ++ ";") (confLines c)) ++
|
||||||
[ "};"
|
[ "};"
|
||||||
, ""
|
, ""
|
||||||
]
|
]
|
||||||
|
@ -57,40 +52,17 @@ zoneStanza z =
|
||||||
cfgline f v = "\t" ++ f ++ " " ++ v ++ ";"
|
cfgline f v = "\t" ++ f ++ " " ++ v ++ ";"
|
||||||
mastersblock =
|
mastersblock =
|
||||||
[ "\tmasters {" ] ++
|
[ "\tmasters {" ] ++
|
||||||
(map (\ip -> "\t\t" ++ ip ++ ";") (zmasters z)) ++
|
(map (\ip -> "\t\t" ++ fromIPAddr ip ++ ";") (confMasters c)) ++
|
||||||
[ "\t};" ]
|
[ "\t};" ]
|
||||||
|
|
||||||
-- | Rewrites the whole named.conf.local file to serve the specificed
|
-- | Rewrites the whole named.conf.local file to serve the specificed
|
||||||
-- zones.
|
-- zones.
|
||||||
zones :: [NamedConf] -> Property
|
servingZones :: [NamedConf] -> Property
|
||||||
zones zs = hasContent namedconf (concatMap zoneStanza zs)
|
servingZones zs = hasContent namedconf (concatMap confStanza zs)
|
||||||
`describe` ("dns server for zones: " ++ unwords (map zoneDesc zs))
|
`describe` ("dns server for zones: " ++ unwords (map zoneDesc zs))
|
||||||
`requires` Apt.serviceInstalledRunning "bind9"
|
`requires` Apt.serviceInstalledRunning "bind9"
|
||||||
`onChange` Service.reloaded "bind9"
|
`onChange` Service.reloaded "bind9"
|
||||||
|
|
||||||
-- | Represents a bind 9 zone file.
|
|
||||||
data Zone = Zone
|
|
||||||
{ zSOA :: SOA
|
|
||||||
, zHosts :: [(HostName, Record)]
|
|
||||||
}
|
|
||||||
deriving (Read, Show, Eq)
|
|
||||||
|
|
||||||
-- | Every domain has a SOA record, which is big and complicated.
|
|
||||||
data SOA = SOA
|
|
||||||
{ sDomain :: BindDomain
|
|
||||||
-- ^ Typically ns1.your.domain
|
|
||||||
, sSerial :: SerialNumber
|
|
||||||
-- ^ The most important parameter is the serial number,
|
|
||||||
-- which must increase after each change.
|
|
||||||
, sRefresh :: Integer
|
|
||||||
, sRetry :: Integer
|
|
||||||
, sExpire :: Integer
|
|
||||||
, sTTL :: Integer
|
|
||||||
, sRecord :: [Record]
|
|
||||||
-- ^ Records for the root of the domain. Typically NS, A, TXT
|
|
||||||
}
|
|
||||||
deriving (Read, Show, Eq)
|
|
||||||
|
|
||||||
-- | Generates a SOA with some fairly sane numbers in it.
|
-- | Generates a SOA with some fairly sane numbers in it.
|
||||||
mkSOA :: Domain -> [Record] -> SOA
|
mkSOA :: Domain -> [Record] -> SOA
|
||||||
mkSOA d rs = SOA
|
mkSOA d rs = SOA
|
||||||
|
@ -105,49 +77,22 @@ mkSOA d rs = SOA
|
||||||
where
|
where
|
||||||
hours n = n * 60 * 60
|
hours n = n * 60 * 60
|
||||||
|
|
||||||
-- | Types of DNS records.
|
|
||||||
--
|
|
||||||
-- This is not a complete list, more can be added.
|
|
||||||
data Record
|
|
||||||
= A Ipv4
|
|
||||||
| AAAA Ipv6
|
|
||||||
| CNAME BindDomain
|
|
||||||
| MX Int BindDomain
|
|
||||||
| NS BindDomain
|
|
||||||
| TXT String
|
|
||||||
deriving (Read, Show, Eq)
|
|
||||||
|
|
||||||
type Ipv4 = String
|
|
||||||
type Ipv6 = String
|
|
||||||
|
|
||||||
-- | Bind serial numbers are unsigned, 32 bit integers.
|
|
||||||
type SerialNumber = CInt
|
|
||||||
|
|
||||||
-- | Domains in the zone file must end with a period if they are absolute.
|
|
||||||
--
|
|
||||||
-- Let's use a type to keep absolute domains straight from relative
|
|
||||||
-- domains.
|
|
||||||
--
|
|
||||||
-- The SOADomain refers to the root SOA record.
|
|
||||||
data BindDomain = RelDomain Domain | AbsDomain Domain | SOADomain
|
|
||||||
deriving (Read, Show, Eq)
|
|
||||||
|
|
||||||
dValue :: BindDomain -> String
|
dValue :: BindDomain -> String
|
||||||
dValue (RelDomain d) = d
|
dValue (RelDomain d) = d
|
||||||
dValue (AbsDomain d) = d ++ "."
|
dValue (AbsDomain d) = d ++ "."
|
||||||
dValue (SOADomain) = "@"
|
dValue (SOADomain) = "@"
|
||||||
|
|
||||||
rField :: Record -> String
|
rField :: Record -> String
|
||||||
rField (A _) = "A"
|
rField (Address (IPv4 _)) = "A"
|
||||||
rField (AAAA _) = "AAAA"
|
rField (Address (IPv6 _)) = "AAAA"
|
||||||
rField (CNAME _) = "CNAME"
|
rField (CNAME _) = "CNAME"
|
||||||
rField (MX _ _) = "MX"
|
rField (MX _ _) = "MX"
|
||||||
rField (NS _) = "NS"
|
rField (NS _) = "NS"
|
||||||
rField (TXT _) = "TXT"
|
rField (TXT _) = "TXT"
|
||||||
|
|
||||||
rValue :: Record -> String
|
rValue :: Record -> String
|
||||||
rValue (A addr) = addr
|
rValue (Address (IPv4 addr)) = addr
|
||||||
rValue (AAAA addr) = addr
|
rValue (Address (IPv6 addr)) = addr
|
||||||
rValue (CNAME d) = dValue d
|
rValue (CNAME d) = dValue d
|
||||||
rValue (MX pri d) = show pri ++ " " ++ dValue d
|
rValue (MX pri d) = show pri ++ " " ++ dValue d
|
||||||
rValue (NS d) = dValue d
|
rValue (NS d) = dValue d
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
module Propellor.Types
|
module Propellor.Types
|
||||||
( Host(..)
|
( Host(..)
|
||||||
, Attr
|
, Attr
|
||||||
, HostName
|
|
||||||
, Propellor(..)
|
, Propellor(..)
|
||||||
, Property(..)
|
, Property(..)
|
||||||
, RevertableProperty(..)
|
, RevertableProperty(..)
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
module Propellor.Types.Attr where
|
module Propellor.Types.Attr where
|
||||||
|
|
||||||
import Propellor.Types.OS
|
import Propellor.Types.OS
|
||||||
|
import qualified Propellor.Types.Dns as Dns
|
||||||
|
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
|
||||||
-- | The attributes of a host. For example, its hostname.
|
-- | The attributes of a host. For example, its hostname.
|
||||||
data Attr = Attr
|
data Attr = Attr
|
||||||
{ _hostname :: HostName
|
{ _hostname :: HostName
|
||||||
, _cnames :: S.Set Domain
|
|
||||||
, _os :: Maybe System
|
, _os :: Maybe System
|
||||||
|
, _dns :: S.Set Dns.Record
|
||||||
, _sshPubKey :: Maybe String
|
, _sshPubKey :: Maybe String
|
||||||
|
|
||||||
, _dockerImage :: Maybe String
|
, _dockerImage :: Maybe String
|
||||||
|
@ -18,8 +19,8 @@ data Attr = Attr
|
||||||
instance Eq Attr where
|
instance Eq Attr where
|
||||||
x == y = and
|
x == y = and
|
||||||
[ _hostname x == _hostname y
|
[ _hostname x == _hostname y
|
||||||
, _cnames x == _cnames y
|
|
||||||
, _os x == _os y
|
, _os x == _os y
|
||||||
|
, _dns x == _dns y
|
||||||
, _sshPubKey x == _sshPubKey y
|
, _sshPubKey x == _sshPubKey y
|
||||||
|
|
||||||
, _dockerImage x == _dockerImage y
|
, _dockerImage x == _dockerImage y
|
||||||
|
@ -30,17 +31,14 @@ instance Eq Attr where
|
||||||
instance Show Attr where
|
instance Show Attr where
|
||||||
show a = unlines
|
show a = unlines
|
||||||
[ "hostname " ++ _hostname a
|
[ "hostname " ++ _hostname a
|
||||||
, "cnames " ++ show (_cnames a)
|
|
||||||
, "OS " ++ show (_os a)
|
, "OS " ++ show (_os a)
|
||||||
|
, "dns " ++ show (_dns a)
|
||||||
, "sshPubKey " ++ show (_sshPubKey a)
|
, "sshPubKey " ++ show (_sshPubKey a)
|
||||||
, "docker image " ++ show (_dockerImage a)
|
, "docker image " ++ show (_dockerImage a)
|
||||||
, "docker run params " ++ show (map (\mk -> mk "") (_dockerRunParams a))
|
, "docker run params " ++ show (map (\mk -> mk "") (_dockerRunParams a))
|
||||||
]
|
]
|
||||||
|
|
||||||
newAttr :: HostName -> Attr
|
newAttr :: HostName -> Attr
|
||||||
newAttr hn = Attr hn S.empty Nothing Nothing Nothing []
|
newAttr hn = Attr hn Nothing S.empty Nothing Nothing []
|
||||||
|
|
||||||
type HostName = String
|
|
||||||
type Domain = String
|
|
||||||
|
|
||||||
type SetAttr = Attr -> Attr
|
type SetAttr = Attr -> Attr
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
module Propellor.Types.Dns where
|
||||||
|
|
||||||
|
import Propellor.Types.OS (HostName)
|
||||||
|
|
||||||
|
import Foreign.C.Types
|
||||||
|
|
||||||
|
type Domain = String
|
||||||
|
|
||||||
|
data IPAddr = IPv4 String | IPv6 String
|
||||||
|
deriving (Read, Show, Eq, Ord)
|
||||||
|
|
||||||
|
fromIPAddr :: IPAddr -> String
|
||||||
|
fromIPAddr (IPv4 addr) = addr
|
||||||
|
fromIPAddr (IPv6 addr) = addr
|
||||||
|
|
||||||
|
-- | Represents a bind 9 named.conf file.
|
||||||
|
data NamedConf = NamedConf
|
||||||
|
{ confDomain :: Domain
|
||||||
|
, confType :: Type
|
||||||
|
, confFile :: FilePath
|
||||||
|
, confMasters :: [IPAddr]
|
||||||
|
, confLines :: [String]
|
||||||
|
}
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
data Type = Master | Secondary
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
-- | Represents a bind 9 zone file.
|
||||||
|
data Zone = Zone
|
||||||
|
{ zSOA :: SOA
|
||||||
|
, zHosts :: [(HostName, Record)]
|
||||||
|
}
|
||||||
|
deriving (Read, Show, Eq)
|
||||||
|
|
||||||
|
-- | Every domain has a SOA record, which is big and complicated.
|
||||||
|
data SOA = SOA
|
||||||
|
{ sDomain :: BindDomain
|
||||||
|
-- ^ Typically ns1.your.domain
|
||||||
|
, sSerial :: SerialNumber
|
||||||
|
-- ^ The most important parameter is the serial number,
|
||||||
|
-- which must increase after each change.
|
||||||
|
, sRefresh :: Integer
|
||||||
|
, sRetry :: Integer
|
||||||
|
, sExpire :: Integer
|
||||||
|
, sTTL :: Integer
|
||||||
|
, sRecord :: [Record]
|
||||||
|
-- ^ Records for the root of the domain. Typically NS, A, TXT
|
||||||
|
}
|
||||||
|
deriving (Read, Show, Eq)
|
||||||
|
|
||||||
|
-- | Types of DNS records.
|
||||||
|
--
|
||||||
|
-- This is not a complete list, more can be added.
|
||||||
|
data Record
|
||||||
|
= Address IPAddr
|
||||||
|
| CNAME BindDomain
|
||||||
|
| MX Int BindDomain
|
||||||
|
| NS BindDomain
|
||||||
|
| TXT String
|
||||||
|
deriving (Read, Show, Eq, Ord)
|
||||||
|
|
||||||
|
-- | Bind serial numbers are unsigned, 32 bit integers.
|
||||||
|
type SerialNumber = CInt
|
||||||
|
|
||||||
|
-- | Domains in the zone file must end with a period if they are absolute.
|
||||||
|
--
|
||||||
|
-- Let's use a type to keep absolute domains straight from relative
|
||||||
|
-- domains.
|
||||||
|
--
|
||||||
|
-- The SOADomain refers to the root SOA record.
|
||||||
|
data BindDomain = RelDomain Domain | AbsDomain Domain | SOADomain
|
||||||
|
deriving (Read, Show, Eq, Ord)
|
|
@ -1,5 +1,6 @@
|
||||||
module Propellor.Types.OS where
|
module Propellor.Types.OS where
|
||||||
|
|
||||||
|
type HostName = String
|
||||||
type UserName = String
|
type UserName = String
|
||||||
type GroupName = String
|
type GroupName = String
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ hosts = -- (o) `
|
||||||
& Ssh.hostKey SshEcdsa
|
& Ssh.hostKey SshEcdsa
|
||||||
& Apt.unattendedUpgrades
|
& Apt.unattendedUpgrades
|
||||||
& Apt.serviceInstalledRunning "ntp"
|
& Apt.serviceInstalledRunning "ntp"
|
||||||
& Dns.zones myDnsSecondary
|
& Dns.servingZones myDnsSecondary
|
||||||
& Postfix.satellite
|
& Postfix.satellite
|
||||||
|
|
||||||
& Apt.serviceInstalledRunning "apache2"
|
& Apt.serviceInstalledRunning "apache2"
|
||||||
|
@ -234,8 +234,8 @@ myDnsSecondary =
|
||||||
, Dns.secondary "branchable.com" branchablemaster
|
, Dns.secondary "branchable.com" branchablemaster
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
master = ["80.68.85.49", "2001:41c8:125:49::10"] -- wren
|
master = [Dns.IPv4 "80.68.85.49", Dns.IPv6 "2001:41c8:125:49::10"] -- wren
|
||||||
branchablemaster = ["66.228.46.55", "2600:3c03::f03c:91ff:fedf:c0e5"]
|
branchablemaster = [Dns.IPv4 "66.228.46.55", Dns.IPv6 "2600:3c03::f03c:91ff:fedf:c0e5"]
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = defaultMain hosts
|
main = defaultMain hosts
|
||||||
|
|
|
@ -99,6 +99,7 @@ Library
|
||||||
Propellor.Exception
|
Propellor.Exception
|
||||||
Propellor.Types
|
Propellor.Types
|
||||||
Propellor.Types.OS
|
Propellor.Types.OS
|
||||||
|
Propellor.Types.Dns
|
||||||
Other-Modules:
|
Other-Modules:
|
||||||
Propellor.Types.Attr
|
Propellor.Types.Attr
|
||||||
Propellor.CmdLine
|
Propellor.CmdLine
|
||||||
|
|
Loading…
Reference in New Issue