@@ -22,6 +22,7 @@ import System.Process (delegate_ctlc, env, proc, readProcess)
2222
2323import Obelisk.App (MonadObelisk)
2424import Obelisk.CLI (Severity (..), callProcessAndLogOutput, failWith, withSpinner)
25+import Obelisk.CLI.Logging
2526import Obelisk.Command.Nix
2627import Obelisk.Command.Thunk
2728import Obelisk.Command.Utils
@@ -45,14 +46,18 @@ deployInit thunkPtr configDir deployDir sshKeyPath hostnames = do
4546 callProcessAndLogOutput (Notice, Error) $
4647 cp [sshKeyPath, localKey]
4748 liftIO $ setPermissions localKey $ setOwnerWritable True $ setOwnerReadable True emptyPermissions
49+ liftIO $ writeFile (deployDir </> "backend_hosts") $ unlines hostnames
50+ forM_ hostnames $ \hostname -> do
51+ putLog Notice $ "Verifying host keys (" <> T.pack hostname <> ")"
52+ verifyHostKey (deployDir </> "backend_known_hosts") localKey hostname
4853 liftIO $ do
4954 createThunk (deployDir </> "src") thunkPtr
50− writeFile (deployDir </> "backend_hosts") $ unlines hostnames
5155 initGit deployDir
5256
5357deployPush :: MonadObelisk m => FilePath -> m ()
5458deployPush deployPath = do
55− host <- liftIO $ fmap (T.unpack . T.strip) $ T.readFile $ deployPath </> "backend_hosts"
59+ let backendHosts = deployPath </> "backend_hosts"
60+ host <- liftIO $ fmap (T.unpack . T.strip) $ T.readFile backendHosts
5661 let srcPath = deployPath </> "src"
5762 build = do
5863 buildOutput <- nixBuild $ def
@@ -78,13 +83,16 @@ deployPush deployPath = do
7883 return Nothing
7984 _ -> return Nothing
8085 forM_ result $ \res -> do
81− let deployAndSwitch outputPath sshAgentEnv = do
86+ let knownHostsPath = deployPath </> "backend_known_hosts"
87+ sshOpts = ["-o", "UserKnownHostsFile=" <> knownHostsPath, "-o", "StrictHostKeyChecking=yes"]
88+ deployAndSwitch outputPath sshAgentEnv = do
8289 withSpinner "Adding ssh key" $ do
8390 callProcess' sshAgentEnv "ssh-add" [deployPath </> "ssh_key"]
8491 withSpinner "Uploading closure" $ do
85− callProcess' sshAgentEnv "nix-copy-closure" ["-v", "--to", "root@" <> host, "--gzip", outputPath]
92+ let nixSshEnv = ("NIX_SSHOPTS", unwords sshOpts) : sshAgentEnv
93+ callProcess' nixSshEnv "nix-copy-closure" ["-v", "--to", "root@" <> host, "--gzip", outputPath]
8694 withSpinner "Switching to new configuration" $ do
87− callProcess' sshAgentEnv "ssh"
95+ callProcess' sshAgentEnv "ssh" $ sshOpts <>
8896 [ "root@" <> host
8997 , unwords
9098 [ "nix-env -p /nix/var/nix/profiles/system --set " <> outputPath
@@ -121,3 +129,12 @@ sshAgent = do
121129 _ <- A.string ";" >> A.endOfLine <|> void (A.string ";")
122130 return (BC8.unpack key, BC8.unpack val)
123131
132+verifyHostKey :: MonadObelisk m => FilePath -> FilePath -> String -> m ()
133+verifyHostKey knownHostsPath keyPath hostName =
134+ callProcessAndLogOutput (Notice, Warning) $ proc "ssh"
135+ [ "-o", "UserKnownHostsFile=" <> knownHostsPath
136+ , "-o", "StrictHostKeyChecking=ask"
137+ , "-i", keyPath
138+ , "root@" <> hostName
139+ , "exit"
140+ ]