diff --git a/README.md b/README.md index 19fbeed..41d781f 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,9 @@ normally support App Connectors. ## Example -Here is a sample config file, in JSON format: +Create a configuration file in either JSON or YAML format. Here’s an example: +`config.json` ```json { "routes": [ @@ -50,6 +51,19 @@ Here is a sample config file, in JSON format: ] } ``` +or `config.yaml` +```yaml +routes: + - "172.16.0.0/22" + - "192.168.0.0/24" +hostRoutes: + - "special-hostname1.example" + - "special-hostname2.example" +awsManagedPrefixLists: + - "pl-02761f4a40454a3c9" +extraArgs: + - "--webclient" +``` Run tailscale-manager: diff --git a/src/TailscaleManager.hs b/src/TailscaleManager.hs index 5513b25..8437481 100644 --- a/src/TailscaleManager.hs +++ b/src/TailscaleManager.hs @@ -40,7 +40,7 @@ helpText = [r|Tailscale routes manager Dynamically resolves a list of hostRoutes to IP addresses, then tells tailscale to advertise them as /32 routes along with any normal CIDR routes. -Config file example: +Config file example (JSON): { "routes": [ @@ -49,7 +49,7 @@ Config file example: ], "hostRoutes": [ "special-hostname1.example", - "special-hostname2.example", + "special-hostname2.example" ], "awsManagedPrefixLists": [ "pl-02761f4a40454a3c9" @@ -60,7 +60,7 @@ Config file example: tsManagerOptions :: Parser TailscaleManagerOptions tsManagerOptions = TailscaleManagerOptions - <$> argument str (metavar "") + <$> argument str (metavar "") <*> switch (long "dryrun" <> help "Dryrun mode") <*> strOption (long "tailscale" @@ -137,7 +137,7 @@ runOnce options prevRoutes = do logDelay return prevRoutes --- |Emit a log message describing the difference between old and new route sets. +-- | Emit a log message describing the difference between old and new route sets. logDiff :: Set IPRange -> Set IPRange -> IO () logDiff prevRoutes newRoutes = do logger <- myLogger @@ -164,8 +164,7 @@ shrinkRatio :: Foldable t -> Double -- ^ Shrink ratio shrinkRatio old new = 1 - (1 / (fromIntegral (length old) / fromIntegral (length new))) --- |Do all the hostname resolution and concat the results with static routes from --- the config. +-- | Generate routes based on config, resolving hostnames and AWS-managed prefix lists. generateRoutes :: TSConfig -> IO (Set IPRange) generateRoutes config = do hostRoutes <- resolveHostnamesToRoutes (tsHostRoutes config) diff --git a/src/TailscaleManager/Config.hs b/src/TailscaleManager/Config.hs index 7b35126..eb27763 100644 --- a/src/TailscaleManager/Config.hs +++ b/src/TailscaleManager/Config.hs @@ -3,15 +3,14 @@ module TailscaleManager.Config where import Data.Aeson +import Data.Aeson (eitherDecodeFileStrict) import Data.Aeson.IP () import Data.ByteString.Lazy qualified as LB import Data.IP (IPRange) import Data.Maybe (fromMaybe) import Data.Text - --- |Parse our config file. May throw AesonException on failure. -loadConfig :: FilePath -> IO TSConfig -loadConfig fp = LB.readFile fp >>= throwDecode +import System.FilePath (takeExtension) +import Data.Yaml (decodeFileEither) -- |Config file schema data TSConfig @@ -35,3 +34,27 @@ instance FromJSON TSConfig where , tsExtraArgs = fromMaybe [] extraArgs , tsAWSManagedPrefixLists = fromMaybe [] awsManagedPrefixLists }) + +-- | Load configuration from a file, detecting format based on file extension. +loadConfig :: FilePath -> IO TSConfig +loadConfig path = do + case takeExtension path of + ".json" -> loadConfigFromJSON path + ".yaml" -> loadConfigFromYAML path + _ -> error "Unsupported file format. Please use .json or .yaml." + +-- | Load configuration from a JSON file. +loadConfigFromJSON :: FilePath -> IO TSConfig +loadConfigFromJSON path = do + result <- eitherDecodeFileStrict path + case result of + Left err -> error $ "Failed to parse JSON config: " ++ err + Right config -> return config + +-- | Load configuration from a YAML file. +loadConfigFromYAML :: FilePath -> IO TSConfig +loadConfigFromYAML path = do + result <- decodeFileEither path + case result of + Left err -> error $ "Failed to parse YAML config: " ++ show err + Right config -> return config diff --git a/tailscale-manager.cabal b/tailscale-manager.cabal index 45bf47b..82f8a19 100644 --- a/tailscale-manager.cabal +++ b/tailscale-manager.cabal @@ -50,6 +50,8 @@ common deps , protolude ^>= 0.3.4 , raw-strings-qq ^>= 1.1 , text ^>= 2.0.2 + , filepath ^>= 1.4.2 + , yaml ^>= 0.11.8 executable tailscale-manager import: warnings, deps