diff --git a/README.md b/README.md new file mode 100644 index 0000000..baa544f --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# CF-DDNS +ddns service for Cloudflare. Currently only supports A and AAAA records. + +## Usage + +This is a WIP project, so no install script is provided yet. You can use the systemd files as a reference to setup your own service. + +### Configuration +cf-ddns is configured using a json file. The default location is `/etc/cf-ddns/config.json`. You can specify a different location using the `-c` flag. + +```json +{ + "cloudflare": { + "token": "...", // Cloudflare API token, requires Zone.DNS.Edit permissions + "zoneId": "..." + }, + "records": [ + { + "name": "ddns.toledompm.xyz", + "proxy": true + }, + ], + "ipv6": { + "enabled": true, + "fetchAddress": "https://myexternalip.com/json" // URL to fetch IPv6 address from, must return a json object with an "ip" field. + }, + "ipv4": { + "enabled": true, + "fetchAddress": "https://myexternalip.com/json" + } +} +``` \ No newline at end of file diff --git a/cmd/ddns.go b/cmd/ddns.go index 34b692d..341fccc 100644 --- a/cmd/ddns.go +++ b/cmd/ddns.go @@ -19,22 +19,33 @@ var ( Short: "Cloudflare DDNS Updater", Long: `Cloudflare DDNS Updater is a simple tool to update Cloudflare DNS records with your current IP address.`, Run: func(cmd *cobra.Command, args []string) { - ipv6, err := nw.GetIPV6() - if err != nil { - fmt.Printf("Error getting IP: %s\n", err) - os.Exit(1) - } - for _, record := range cfg.Records { if cfg.IPV6.Enabled { + ipv6, err := nw.GetIPV6() + if err != nil { + fmt.Printf("Error getting IP: %s\n", err) + os.Exit(1) + } + err = cf.UpdateRecord(ipv6, record.Name, cfg.Cloudflare.ZoneID, record.Proxy, "AAAA") if err != nil { fmt.Printf("Error updating record: %s\n", err) os.Exit(1) } - } else { - fmt.Println("Only ipv6 is supported at this time") - os.Exit(1) + } + + if cfg.IPV4.Enabled { + ipv4, err := nw.GetIPV4() + if err != nil { + fmt.Printf("Error getting IP: %s\n", err) + os.Exit(1) + } + + err = cf.UpdateRecord(ipv4, record.Name, cfg.Cloudflare.ZoneID, record.Proxy, "A") + if err != nil { + fmt.Printf("Error updating record: %s\n", err) + os.Exit(1) + } } } }, @@ -50,7 +61,7 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "(required) path to config file") + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "/etc/cf-ddns/config.json", "(required) path to config file") } func initConfig() { @@ -69,5 +80,5 @@ func initConfig() { cfg = config.MustParseConfig(cfgFileBytes, cfg) cf = handlers.NewCloudflare(cfg.Cloudflare.Token) - nw = handlers.NewNetwork(cfg.IPV6.FetchAddress) + nw = handlers.NewNetwork(cfg.IPV6.FetchAddress, cfg.IPV4.FetchAddress) } diff --git a/config/config.go b/config/config.go index fdcc399..7a7c931 100644 --- a/config/config.go +++ b/config/config.go @@ -26,6 +26,13 @@ type Config struct { // FetchAddress is the URL to fetch the current IPV6 address FetchAddress string `json:"fetchAddress"` } `json:"ipv6"` + + IPV4 struct { + // Enabled is a flag to enable or disable IPV4 + Enabled bool `json:"enabled"` + // FetchAddress is the URL to fetch the current IPV4 address + FetchAddress string `json:"fetchAddress"` + } `json:"ipv4"` } func New() *Config { diff --git a/handlers/network.go b/handlers/network.go index 827963d..ef5ce9c 100644 --- a/handlers/network.go +++ b/handlers/network.go @@ -8,24 +8,35 @@ import ( type Network interface { GetIPV6() (string, error) + GetIPV4() (string, error) } type ipResponse struct { IP string `json:"ip"` } -func NewNetwork(ipv6FetchAddress string) Network { +func NewNetwork(ipv6FetchAddress string, ipv4FetchAddress string) Network { return &NetworkHandler{ ipv6FetchAddress: ipv6FetchAddress, + ipv4FetchAddress: ipv4FetchAddress, } } type NetworkHandler struct { ipv6FetchAddress string + ipv4FetchAddress string +} + +func (n *NetworkHandler) GetIPV4() (string, error) { + return getIP(n.ipv4FetchAddress) } func (n *NetworkHandler) GetIPV6() (string, error) { - httpResponse, err := http.Get(n.ipv6FetchAddress) + return getIP(n.ipv6FetchAddress) +} + +func getIP(apiAddr string) (string, error) { + httpResponse, err := http.Get(apiAddr) if err != nil { return "", err }