1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
-- This is the main configuration file for Propellor, and is used to
-- the propellor program.
import Propellor
import qualified Propellor.Property.Apt as Apt
import qualified Propellor.Property.Cmd as Cmd
import qualified Propellor.Property.Cron as Cron
import qualified Propellor.Property.File as File
import qualified Propellor.Property.Sudo as Sudo
import qualified Propellor.Property.User as User
import qualified Propellor.Property.Ssh as Ssh
import qualified Propellor.Property.Hostname as Hostname
import qualified Propellor.Property.Locale as Locale
import Control.Applicative
import Propellor.Utilities
import Data.Maybe
import Data.List
main :: IO ()
main = defaultMain hosts
-- The hosts propellor knows about.
hosts :: [Host]
hosts =
[
-- cavia,
coypu,
marmot,
woodmouse,
wolverine
]
-- Cavia is a T430s laptop - my main terminal
-- Stable
-- cavia :: Host
-- cavia = host "cavia.rodere.systems" $ props
-- & standardGraphicalSystem (Stable "stretch") X86_64 ["Welcome to Cavia"]
-- Coypu is a HTPC low-power i3 server
-- Testing
coypu :: Host
coypu = host "coypu.rodere.systems" $ props
& standardSystem (Stable "stretch") X86_64 ["Welcome to Coypu"]
-- Wolverine is a powerful desktop server that usually stays powered off
-- Good for everything
-- Testing
wolverine :: Host
wolverine = host "wolverine.rodere.systems" $ props
& standardSystem (Testing) X86_64 ["Welcome to Wolverine"]
-- Marmot is a well-rounded server for development and builds.
-- Semi-stable
marmot :: Host
marmot = host "marmot.rodere.systems" $ props
& standardSystem (Stable "stretch") X86_64 ["Welcome to Marmot"]
& Apt.installed ["cgit", "nginx", "fcgiwrap"]
-- Woodmouse is a minimal workbench server
-- Stable
woodmouse :: Host
woodmouse = host "woodmouse.rodere.systems" $ props
& standardSystem (Stable "stretch") X86_64 ["Welcome to Woodmouse"]
& Apt.backportInstalled["znc"]
& customZNCBacklogPkgInstalled
-- This is my standard system setup.
type Motd = [String]
-- This is my standard system setup.
standardSystem :: DebianSuite -> Architecture -> Motd -> Property (HasInfo + Debian)
standardSystem suite arch motd =
standardSystemUnhardened suite arch motd
`before` Ssh.noPasswords
standardSystemUnhardened :: DebianSuite -> Architecture -> Motd -> Property (HasInfo + Debian)
standardSystemUnhardened suite arch motd = propertyList "standard system" $ props
& osDebian suite arch
& Hostname.sane
& Hostname.searchDomain
& Locale.available "en_US.UTF-8"
& File.hasContent "/etc/motd" ("":motd++[""])
& Apt.stdSourcesList `onChange` Apt.upgrade
& Apt.cacheCleaned
& Apt.update
& Apt.installed ["etckeeper","sudo"]
& Apt.installed ["ssh","mosh","git","wget","curl"]
& Apt.installed ["vim","screen","less","emacs-nox"]
& User.accountFor (User "root")
& User.hasSomePassword (User "root")
& User.accountFor (User "gnusosa")
& User.hasSomePassword (User "gnusosa")
& Sudo.enabledFor (User "gnusosa")
& User.accountFor (User "git")
& Cron.runPropellor (Cron.Times "20 * * * *")
-- I don't care for Exim
& Apt.removed ["exim4", "exim4-daemon-light", "exim4-config", "exim4-base"]
`onChange` Apt.autoRemove
customZNCBacklogPkgInstalled :: Property UnixLike
customZNCBacklogPkgInstalled = check (not <$> Apt.isInstalled "znc-backlog") (getAndInstallZNCBacklogPkg)
getWebFile :: [String] -> String -> UncheckedProperty UnixLike
getWebFile params url = cmdProperty "wget" (params++[url])
doesWebFileExist :: String -> IO Bool
doesWebFileExist url = (== [True]) <$> (mapMaybe parse . lines) <$> go
where
parse l
| "HTTP/1.1 200 OK" `isInfixOf` l = Just True
| "HTTP/1.1 400 Not Found" `isInfixOf` l = Just False
| otherwise = Nothing
go = do
readProcess "curl" (["--head","-L"]++[url])
runForcedDpkg :: Apt.Package -> [String] -> Property UnixLike
runForcedDpkg ps fs = check (Apt.isInstalled ps) (cmdProperty "dpkg" (["-i", "--force-all"]++fs))
getZNCBacklogPkg :: Property UnixLike
getZNCBacklogPkg = check (doesWebFileExist url) (getWebFile ["-Pznc"] url)
where
url = "http://www.gnusosa.net/misc/pkgs/debian/znc-backlog.deb"
installZNCBacklogPkg :: Property UnixLike
installZNCBacklogPkg = check (doesFileExist path) (runForcedDpkg "znc-backlog" [path])
where
path = "/usr/local/propellor/znc/znc-backlog.deb"
getAndInstallZNCBacklogPkg :: Property UnixLike
getAndInstallZNCBacklogPkg = propertyList "get-install custom znc-backlog package" $ props
& getZNCBacklogPkg
& installZNCBacklogPkg
|