From b5cd2bdcd2ed1e0fab4893d68ec7778a13ebc1a4 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Sat, 1 Jan 2022 19:27:22 +0100 Subject: [PATCH 1/6] Refacto Github actions --- .github/workflows/dev.yaml | 25 +++++++++++++++++++ .github/workflows/main.yml | 45 ----------------------------------- .github/workflows/page.yml | 0 .github/workflows/release.yml | 38 +++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/dev.yaml delete mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/page.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/dev.yaml b/.github/workflows/dev.yaml new file mode 100644 index 0000000..f906837 --- /dev/null +++ b/.github/workflows/dev.yaml @@ -0,0 +1,25 @@ +--- +name: build on push dev + +on: + push: + branches: + - "dev" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set image tag + run: | + TAG="dev" + echo "TAG=${TAG}" >> $GITHUB_ENV + - name: create and push an image + run: > + buildah unshare bash ./build.sh + ${{ env.TAG }} + ${{ secrets.REGISTRY_USERNAME }} + ${{ secrets.REGISTRY_PASSWORD }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index d6b1b5e..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: build OCI image - -on: - push: - branches: - - "*" - - "**" - - "!dependabot/**" - release: - types: # This configuration does not affect the page_build event above - - created - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Get latest release version number - id: get_version - uses: battila7/get-version-action@v2 - - name: Set image tag - run: | - if [[ "${{ steps.get_version.outputs.version }}" != "" && "${{ steps.get_version.outputs.version }}" != "master" ]]; then - TAG="${{ steps.get_version.outputs.version }}" - else - TAG="dev" - fi - echo "TAG=${TAG}" >> $GITHUB_ENV - # - name: GitGuardian scan - # uses: GitGuardian/gg-shield-action@master - # env: - # GITHUB_PUSH_BEFORE_SHA: ${{ github.event.before }} - # GITHUB_PUSH_BASE_SHA: ${{ github.event.base }} - # GITHUB_PULL_BASE_SHA: ${{ github.event.pull_request.base.sha }} - # GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - # GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }} - - name: create and push an image - run: > - buildah unshare bash ./build.sh - ${{ env.TAG }} - ${{ secrets.REGISTRY_USERNAME }} - ${{ secrets.REGISTRY_PASSWORD }} diff --git a/.github/workflows/page.yml b/.github/workflows/page.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5fdbf93 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +--- +name: build on release + +on: + release: + types: # This configuration does not affect the page_build event above + - created + +jobs: + page: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.x + - run: pip install mkdocs-material + + - run: mkdocs gh-deploy --force + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Get latest release version number + id: get_version + uses: battila7/get-version-action@v2 + - name: Set image tag + run: | + TAG="${{ steps.get_version.outputs.version }}" + echo "TAG=${TAG}" >> $GITHUB_ENV + - name: create and push an image + run: > + buildah unshare bash ./build.sh + ${{ env.TAG }} + ${{ secrets.REGISTRY_USERNAME }} + ${{ secrets.REGISTRY_PASSWORD }} From 9052b4a3e7860a3eb038d6149c8ae881e1679956 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Sat, 1 Jan 2022 19:28:57 +0100 Subject: [PATCH 2/6] BREAKING CHANGE - Refacto config file --- config/config.go | 122 ++++++++++++++++++++++++++++++++++------------- main.go | 27 ++++++----- 2 files changed, 103 insertions(+), 46 deletions(-) diff --git a/config/config.go b/config/config.go index b3f0961..9d01bc7 100644 --- a/config/config.go +++ b/config/config.go @@ -37,22 +37,24 @@ type CFG struct { Level string `yaml:"level"` Humanize bool `yaml:"humanize"` } - Metrics CFGMetrics `yaml:"metrics"` - AWSAccount struct { - Enable bool `yaml:"enable"` - Secret uip_aws.PawsSecret `yaml:"secret"` - Record uip_aws.PawsRecord `yaml:"record"` - } `yaml:"aws_account"` - OVHAccount struct { - Enable bool `yaml:"enable"` - Secret uip_ovh.PovhSecret `yaml:"secret"` - Record uip_ovh.PovhRecord `yaml:"record"` - } `yaml:"ovh_account"` - CLOUDFLAREAccount struct { - Enable bool `yaml:"enable"` - Secret uip_cloudflare.PCloudflareSecret `yaml:"secret"` - Record uip_cloudflare.PCloudflareRecord `yaml:"record"` - } `yaml:"cloudflare_account"` + Metrics CFGMetrics `yaml:"metrics"` + Providers struct { + AWSAccount struct { + Enable bool `yaml:"enable"` + Secret uip_aws.PawsSecret `yaml:"secret"` + Record uip_aws.PawsRecord `yaml:"record"` + } `yaml:"aws"` + OVHAccount struct { + Enable bool `yaml:"enable"` + Secret uip_ovh.PovhSecret `yaml:"secret"` + Record uip_ovh.PovhRecord `yaml:"record"` + } `yaml:"ovh"` + CLOUDFLAREAccount struct { + Enable bool `yaml:"enable"` + Secret uip_cloudflare.PCloudflareSecret `yaml:"secret"` + Record uip_cloudflare.PCloudflareRecord `yaml:"record"` + } `yaml:"cloudflare"` + } `yaml:"providers"` } // LoadConfig reads configuration from file or environment variables. @@ -90,29 +92,39 @@ func LoadConfig() (config CFG) { var ok bool // try to read from environment variables + // env AWS_ACCOUNT_ENABLE + if _, ok = os.LookupEnv("AWS_ACCOUNT_ENABLE"); ok { + log.Info().Msg("AWS_ACCOUNT_ENABLE found in environment variables") + config.Providers.AWSAccount.Enable, err = strconv.ParseBool(os.Getenv("AWS_ACCOUNT_ENABLE")) + + if err != nil { + log.Error().Err(err).Msg("Failed to parse AWS_ACCOUNT_ENABLE") + } + } + if _, ok = os.LookupEnv("AWS_ACCESS_KEY_ID"); ok { log.Info().Msg("Reading AWS_ACCESS_KEY_ID from environment variables") - config.AWSAccount.Secret.AccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID") + config.Providers.AWSAccount.Secret.AccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID") } if _, ok = os.LookupEnv("AWS_SECRET_ACCESS_KEY"); ok { log.Info().Msg("Reading AWS_SECRET_ACCESS_KEY from environment variables") - config.AWSAccount.Secret.SecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY") + config.Providers.AWSAccount.Secret.SecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY") } if _, ok = os.LookupEnv("AWS_REGION"); ok { log.Info().Msg("Reading AWS_REGION from environment variables") - config.AWSAccount.Secret.Region = os.Getenv("AWS_REGION") + config.Providers.AWSAccount.Secret.Region = os.Getenv("AWS_REGION") } if _, ok = os.LookupEnv("AWS_HOSTED_ZONE_ID"); ok { log.Info().Msg("Reading AWS_HOSTED_ZONE_ID from environment variables") - config.AWSAccount.Record.HostedZoneID = os.Getenv("AWS_HOSTED_ZONE_ID") + config.Providers.AWSAccount.Record.HostedZoneID = os.Getenv("AWS_HOSTED_ZONE_ID") } if _, ok = os.LookupEnv("AWS_RECORD_NAME"); ok { log.Info().Msg("Reading AWS_RECORD_NAME from environment variables") - config.AWSAccount.Record.Name = os.Getenv("AWS_HOSTED_ZONE_NAME") + config.Providers.AWSAccount.Record.Name = os.Getenv("AWS_HOSTED_ZONE_NAME") } // AWS_RECORD_TTL @@ -122,44 +134,59 @@ func LoadConfig() (config CFG) { if err != nil { log.Error().Err(err).Str("value", os.Getenv("AWS_RECORD_TTL")).Msg("Failed to convert AWS_RECORD_TTL to int") } else { - config.AWSAccount.Record.TTL = ttl + config.Providers.AWSAccount.Record.TTL = ttl } } // AWS_RECORD_DOMAIN if _, ok = os.LookupEnv("AWS_RECORD_DOMAIN"); ok { log.Info().Msg("Reading AWS_RECORD_DOMAIN from environment variables") - config.AWSAccount.Record.Domain = os.Getenv("AWS_RECORD_DOMAIN") + config.Providers.AWSAccount.Record.Domain = os.Getenv("AWS_RECORD_DOMAIN") } // AWS_RECORD_COMMENT if _, ok = os.LookupEnv("AWS_RECORD_COMMENT"); ok { log.Info().Msg("Reading AWS_RECORD_COMMENT from environment variables") - config.AWSAccount.Record.Comment = os.Getenv("AWS_RECORD_COMMENT") + config.Providers.AWSAccount.Record.Comment = os.Getenv("AWS_RECORD_COMMENT") + } + + // OVH_ACCOUNT_ENABLE + if _, ok = os.LookupEnv("OVH_ACCOUNT_ENABLE"); ok { + log.Info().Msg("OVH_ACCOUNT_ENABLE found in environment variables") + config.Providers.OVHAccount.Enable, err = strconv.ParseBool(os.Getenv("OVH_ACCOUNT_ENABLE")) + if err != nil { + log.Error().Err(err).Msg("Failed to parse OVH_ACCOUNT_ENABLE") + } } // OVH_APPLICATION_KEY if _, ok = os.LookupEnv("OVH_APPLICATION_KEY"); ok { log.Info().Msg("Reading OVH_APPLICATION_KEY from environment variables") - config.OVHAccount.Secret.ApplicationKey = os.Getenv("OVH_APPLICATION_KEY") + config.Providers.OVHAccount.Secret.ApplicationKey = os.Getenv("OVH_APPLICATION_KEY") } // OVH_APPLICATION_SECRET if _, ok = os.LookupEnv("OVH_APPLICATION_SECRET"); ok { log.Info().Msg("Reading OVH_APPLICATION_SECRET from environment variables") - config.OVHAccount.Secret.ApplicationSecret = os.Getenv("OVH_APPLICATION_SECRET") + config.Providers.OVHAccount.Secret.ApplicationSecret = os.Getenv("OVH_APPLICATION_SECRET") } // OVH_CONSUMER_KEY if _, ok = os.LookupEnv("OVH_CONSUMER_KEY"); ok { log.Info().Msg("Reading OVH_CONSUMER_KEY from environment variables") - config.OVHAccount.Secret.ConsumerKey = os.Getenv("OVH_CONSUMER_KEY") + config.Providers.OVHAccount.Secret.ConsumerKey = os.Getenv("OVH_CONSUMER_KEY") + } + + // OVH_REGION + if _, ok = os.LookupEnv("OVH_REGION"); ok { + log.Info().Msg("Reading OVH_REGION from environment variables") + config.Providers.OVHAccount.Secret.Region = os.Getenv("OVH_REGION") } // OVH_RECORD_NAME if _, ok = os.LookupEnv("OVH_RECORD_NAME"); ok { log.Info().Msg("Reading OVH_RECORD_NAME from environment variables") - config.OVHAccount.Record.Name = os.Getenv("OVH_RECORD_NAME") + config.Providers.OVHAccount.Record.Name = os.Getenv("OVH_RECORD_NAME") } // OVH_RECORD_TTL @@ -169,34 +196,55 @@ func LoadConfig() (config CFG) { if err != nil { log.Error().Err(err).Str("value", os.Getenv("OVH_RECORD_TTL")).Msg("Failed to convert OVH_RECORD_TTL to int") } else { - config.OVHAccount.Record.TTL = ttl + config.Providers.OVHAccount.Record.TTL = ttl } } // OVH_RECORD_ZONE if _, ok = os.LookupEnv("OVH_RECORD_ZONE"); ok { log.Info().Msg("Reading OVH_RECORD_ZONE from environment variables") - config.OVHAccount.Record.Zone = os.Getenv("OVH_RECORD_ZONE") + config.Providers.OVHAccount.Record.Zone = os.Getenv("OVH_RECORD_ZONE") + } + + // CLOUDFLARE_ACCOUNT_ENABLE + if _, ok = os.LookupEnv("CLOUDFLARE_ACCOUNT_ENABLE"); ok { + log.Info().Msg("CLOUDFLARE_ACCOUNT_ENABLE found in environment variables") + config.Providers.CLOUDFLAREAccount.Enable, err = strconv.ParseBool(os.Getenv("CLOUDFLARE_ACCOUNT_ENABLE")) + if err != nil { + log.Error().Err(err).Msg("Failed to parse CLOUDFLARE_ACCOUNT_ENABLE") + } } // CLOUDFLARE_API_KEY if _, ok = os.LookupEnv("CLOUDFLARE_API_KEY"); ok { log.Info().Msg("Reading CLOUDFLARE_API_KEY from environment variables") - config.CLOUDFLAREAccount.Secret.APIKey = os.Getenv("CLOUDFLARE_API_KEY") + config.Providers.CLOUDFLAREAccount.Secret.APIKey = os.Getenv("CLOUDFLARE_API_KEY") } // CLOUDFLARE_EMAIL if _, ok = os.LookupEnv("CLOUDFLARE_EMAIL"); ok { log.Info().Msg("Reading CLOUDFLARE_EMAIL from environment variables") - config.CLOUDFLAREAccount.Secret.Email = os.Getenv("CLOUDFLARE_EMAIL") + config.Providers.CLOUDFLAREAccount.Secret.Email = os.Getenv("CLOUDFLARE_EMAIL") } // CLOUDFLARE_RECORD_NAME if _, ok = os.LookupEnv("CLOUDFLARE_RECORD_NAME"); ok { log.Info().Msg("Reading CLOUDFLARE_RECORD_NAME from environment variables") - config.CLOUDFLAREAccount.Record.Name = os.Getenv("CLOUDFLARE_RECORD_NAME") + config.Providers.CLOUDFLAREAccount.Record.Name = os.Getenv("CLOUDFLARE_RECORD_NAME") } + // CLOUDFLARE_RECORD_DOMAIN + if _, ok = os.LookupEnv("CLOUDFLARE_RECORD_DOMAIN"); ok { + log.Info().Msg("Reading CLOUDFLARE_RECORD_DOMAIN from environment variables") + config.Providers.CLOUDFLAREAccount.Record.Domain = os.Getenv("CLOUDFLARE_RECORD_DOMAIN") + } + + // CLOUDFLARE_RECORD_ZONEID + if _, ok = os.LookupEnv("CLOUDFLARE_RECORD_ZONEID"); ok { + log.Info().Msg("Reading CLOUDFLARE_RECORD_ZONEID from environment variables") + config.Providers.CLOUDFLAREAccount.Record.ZoneID = os.Getenv("CLOUDFLARE_RECORD_ZONEID") + + // CLOUDFLARE_RECORD_TTL if _, ok = os.LookupEnv("CLOUDFLARE_RECORD_TTL"); ok { log.Info().Msg("Reading CLOUDFLARE_RECORD_TTL from environment variables") @@ -204,7 +252,7 @@ func LoadConfig() (config CFG) { if err != nil { log.Error().Err(err).Str("value", os.Getenv("CLOUDFLARE_RECORD_TTL")).Msg("Failed to convert CLOUDFLARE_RECORD_TTL to int") } else { - config.CLOUDFLAREAccount.Record.TTL = ttl + config.Providers.CLOUDFLAREAccount.Record.TTL = ttl } } @@ -238,6 +286,12 @@ func LoadConfig() (config CFG) { } } + // METRICS ADDRESS + if _, ok = os.LookupEnv("METRICS_HOST"); ok { + log.Info().Msg("Reading METRICS_ADDRESS from environment variables") + config.Metrics.Host = os.Getenv("METRICS_HOST") + } + // METRICS_PORT if _, ok = os.LookupEnv("METRICS_PORT"); ok { log.Info().Msg("Reading METRICS_PORT from environment variables") diff --git a/main.go b/main.go index 2a273d7..1f43b6b 100644 --- a/main.go +++ b/main.go @@ -36,9 +36,10 @@ func main() { log.Logger = zerolog.New(os.Stderr).With().Timestamp().Logger() } - log.Info().Msg("Starting UpdateIP") - // Parse loglevel + if c.Log.Level == "" { + c.Log.Level = "info" + } if l, err := zerolog.ParseLevel(c.Log.Level); err != nil { log.Fatal().Err(err).Msg("cannot parse log level") } else { @@ -46,11 +47,13 @@ func main() { log.Info().Msgf("Log level set to %s", l.String()) } - if c.AWSAccount.Enable { + log.Info().Msg("Starting UpdateIP") + + if c.Providers.AWSAccount.Enable { Paws = uip_aws.Paws{ - Record: c.AWSAccount.Record, - Secret: c.AWSAccount.Secret, + Record: c.Providers.AWSAccount.Record, + Secret: c.Providers.AWSAccount.Secret, } if err := Paws.NewClient(); err != nil { @@ -65,11 +68,11 @@ func main() { } - if c.OVHAccount.Enable { + if c.Providers.OVHAccount.Enable { Povh = uip_ovh.Povh{ - Record: c.OVHAccount.Record, - Secret: c.OVHAccount.Secret, + Record: c.Providers.OVHAccount.Record, + Secret: c.Providers.OVHAccount.Secret, } if err := Povh.NewClient(); err != nil { @@ -83,11 +86,11 @@ func main() { } - if c.CLOUDFLAREAccount.Enable { + if c.Providers.CLOUDFLAREAccount.Enable { PCloudflare = uip_cloudflare.PCloudflare{ - Record: c.CLOUDFLAREAccount.Record, - Secret: c.CLOUDFLAREAccount.Secret, + Record: c.Providers.CLOUDFLAREAccount.Record, + Secret: c.Providers.CLOUDFLAREAccount.Secret, } if err := PCloudflare.NewClient(); err != nil { @@ -105,7 +108,7 @@ func main() { if c.Metrics.Enable { log.Info().Msg("Starting Metrics Server") m = metrics.Init(c.Metrics) - if c.AWSAccount.Enable { + if c.Providers.AWSAccount.Enable { m.RegisterPkg(Paws.RegistryMetrics()) } From 35c7ad2cc87508b4e506bab0231af811d85b4fa7 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Sat, 1 Jan 2022 19:30:15 +0100 Subject: [PATCH 3/6] Write New Documentation --- README.md | 71 +++++++++++++++------------- docs/advanced/dns.md | 58 +++++++++++++++++++++++ docs/getting-started.md | 31 ++++++++++++ docs/index.md | 10 ++++ docs/providers/aws.md | 3 ++ docs/providers/cloudflare.md | 3 ++ docs/providers/ovh.md | 3 ++ docs/setup/configfile.md | 91 ++++++++++++++++++++++++++++++++++++ docs/setup/docker-compose.md | 33 +++++++++++++ docs/setup/docker.md | 36 ++++++++++++++ docs/setup/envvars.md | 39 ++++++++++++++++ docs/setup/source.md | 16 +++++++ mkdocs.yml | 50 ++++++++++++++++++++ 13 files changed, 412 insertions(+), 32 deletions(-) create mode 100644 docs/advanced/dns.md create mode 100644 docs/getting-started.md create mode 100644 docs/index.md create mode 100644 docs/providers/aws.md create mode 100644 docs/providers/cloudflare.md create mode 100644 docs/providers/ovh.md create mode 100644 docs/setup/configfile.md create mode 100644 docs/setup/docker-compose.md create mode 100644 docs/setup/docker.md create mode 100644 docs/setup/envvars.md create mode 100644 docs/setup/source.md create mode 100644 mkdocs.yml diff --git a/README.md b/README.md index d472819..d3cc4de 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ UpdateIP is a automatic update of a DNS record based on the External IP address +Actual supported DNS providers: + +* Amazon Route 53 +* Cloudflare +* OVH (Warning: Is not tested) + ## How to setup ### **From Docker Run** @@ -48,36 +54,37 @@ metrics: host: 0.0.0.0 # Default: 0.0.0.0 path: /metrics # Default: /metrics -aws_account: - enable: true - secret: - access_key_id: "xxx" - secret_access_key: "xxx" - region: "eu-west-1" - record: - name: "subdomain.domain.com" - ttl: 60 - domain: "domain.com" - -ovh_account: - enable: true - secret: - application_key: "xxx" - application_secret: "xxx" - consumer_key: "xxx" - region: "eu-west-1" - record: - name: "subdomain.domain.com" - ttl: 60 - zone: "domain.com" - -cloudflare_account: - enable: true - secret: - api_key: "xxx" - email: "xxx" - record: - name: "subdomain.domain.com" - ttl: 60 - domain: "domain.com" +providers: + aws_account: + enable: true + secret: + access_key_id: "xxx" + secret_access_key: "xxx" + region: "eu-west-1" + record: + name: "subdomain.domain.com" + ttl: 60 + domain: "domain.com" + + ovh_account: + enable: true + secret: + application_key: "xxx" + application_secret: "xxx" + consumer_key: "xxx" + region: "eu-west-1" + record: + name: "subdomain.domain.com" + ttl: 60 + zone: "domain.com" + + cloudflare_account: + enable: true + secret: + api_key: "xxx" + email: "xxx" + record: + name: "subdomain.domain.com" + ttl: 60 + domain: "domain.com" ``` diff --git a/docs/advanced/dns.md b/docs/advanced/dns.md new file mode 100644 index 0000000..20473c4 --- /dev/null +++ b/docs/advanced/dns.md @@ -0,0 +1,58 @@ +# Advanced - DNS + +OpenDNS offer service to get your external IP address by DNS query. UpdateIP use this service to get your external IP address. +This is a representation of the DNS Client in bash. + +```bash +dig +short myip.opendns.com @208.67.222.222 +``` + +```go +// GetMyExternalIP returns the external IP address of the local machine. +func GetMyExternalIP() (ip net.IP, err error) { + r := &net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + d := net.Dialer{ + Timeout: time.Millisecond * time.Duration(10000), + } + return d.DialContext(ctx, network, "208.67.222.222:53") + }, + } + xIP, err := r.LookupHost(context.Background(), "myip.opendns.com") + if err != nil { + return net.IP{}, err + } + return net.ParseIP(xIP[0]), err +} +``` + +UpdateIP use also the DNS Client to get status of the DNS record. +The configuration of the DNS server used is therefore important. + +If you running UpdateIP from a Docker container, you can use the following command to get the configuration of the DNS server used: + +On `docker run` command use `--dns x.x.x.x` to set manual DNS server for this container. +On Docker compose use a following parameters to set manual DNS server for this container. + +```bash +version: '3.8' + +services: + updateip: + container_name: updateaip + image: ghcr.io/azrod/updateip:latest + volumes: + - ./config.yaml:/config/config.yaml:ro + dns: + - x.x.x.x + environnement: + - LOG_LEVEL=debug + - LOG_HUMANIZE=true + - METRICS_ENABLE=true + - METRICS_PORT=8080 + - METRICS_HOST=0.0.0.0 + port: + - "8080:8080" + restart: always +``` diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..899380e --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,31 @@ +# Getting started + + +## Docker + +The easiest and fastest way to get started with updateip is to use its docker image. +You can run the image with the following command : + +```bash + +docker run -itd \ + -v "./config.yaml:/config/config.yaml" \ + ghcr.io/azrod/updateip:latest + +``` + +## Basic configuration + +Create the **config.yaml** configuration file + +```yaml +log: + level: debug # Available : trace debug info warn error fatal panic + humanize: true # Default: false + +# >> Here Setup your provider << +``` + +## Setup your provider + +Go to the [provider documentation](providers.md) to setup your provider. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..c30bef7 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,10 @@ +# Overview + +Welcome to the **UpdateIP** documentation site + +UpdateIP dynamically updates a record based on its public IP address. It is compatible with many DNS service providers. + +## What is UpdateIP ? + +On a regular basis the IP address registered in the DNS server and the current public IP address and is updated if they are different. +UpdateIP is written 100% in golang, it is light and can be executed on all types of X86, ARM architecture. diff --git a/docs/providers/aws.md b/docs/providers/aws.md new file mode 100644 index 0000000..f3d3102 --- /dev/null +++ b/docs/providers/aws.md @@ -0,0 +1,3 @@ +# AWS Route 53 Provider + +## shortly diff --git a/docs/providers/cloudflare.md b/docs/providers/cloudflare.md new file mode 100644 index 0000000..9f3e034 --- /dev/null +++ b/docs/providers/cloudflare.md @@ -0,0 +1,3 @@ +# Cloudflare Provider + +## shortly diff --git a/docs/providers/ovh.md b/docs/providers/ovh.md new file mode 100644 index 0000000..d387fa0 --- /dev/null +++ b/docs/providers/ovh.md @@ -0,0 +1,3 @@ +# OVH Provider + +## shortly diff --git a/docs/setup/configfile.md b/docs/setup/configfile.md new file mode 100644 index 0000000..78c2df2 --- /dev/null +++ b/docs/setup/configfile.md @@ -0,0 +1,91 @@ +# Configuration File + +See all options available in configuration file. + +By default Update loads the configuration file into the `/config` directory and the configuration file is called `config.yaml`. +it is possible to change the directory and the configuration file which are called when the program starts. + +* `PATH_CONFIG_DIRECTORY` : directory where the configuration file is located. +* `PATH_CONFIG_FILE` : configuration file name. + + +## All options available + +| Options | Default | Required | Actions | +| --------------------------------------- | -------- | ------------------------ | --------------------------------------------------------------- | +| log.level | info | :heavy_multiplication_x: | Set Log Level *(accept trace, debug, info, warn, error, fatal)* | +| log.humanize | false | :heavy_multiplication_x: | Set human log format | +| | | | | +| metrics.enable | false | :heavy_multiplication_x: | Define if start metrics web server | +| metrics.host | 0.0.0.0 | :heavy_multiplication_x: | Set IP address for metrics web server | +| metrics.port | 8080 | :heavy_multiplication_x: | Set port for metrics web server | +| metrics.path | /metrics | :heavy_multiplication_x: | Path for acceding to metrics web server | +| | | | | +| providers.aws.enable | false | :heavy_multiplication_x: | Enable AWS Route 53 Provider | +| providers.aws.secret.access_key_id | "" | :heavy_check_mark: | AccessKey for AWS Account | +| providers.aws.secret.secret_access_key | "" | :heavy_check_mark: | SecretKey for AWS Account | +| providers.aws.secret.region | "" | :heavy_check_mark: | Region for your domain | +| providers.aws.record.name | "" | :heavy_check_mark: | FQDN record *(ex.domain.com)* | +| providers.aws.record.domain | "" | :heavy_check_mark: | Domain Name *(domain.com)* | +| providers.aws.record.hosted_zone_id | "" | :heavy_multiplication_x: | HostedZoneID of your domain | +| | | | | +| providers.ovh.enable | false | :heavy_multiplication_x: | Enable OVH Provider | +| providers.ovh.secret.application_key | "" | :heavy_check_mark: | Application Key for OVH Account | +| providers.ovh.secret.application_secret | "" | :heavy_check_mark: | Application Secret for OVH Account | +| providers.ovh.secret.consumer_key | "" | :heavy_check_mark: | Consumer Key for OVH Account | +| providers.ovh.secret.region | "" | :heavy_check_mark: | Region for your domain | +| providers.ovh.record.name | "" | :heavy_check_mark: | FQDN record *(ex.domain.com)* | +| providers.ovh.record.zone | "" | :heavy_check_mark: | DNS Zone *(domain.com)* | +| | | | | +| providers.cloudflare.enable | false | :heavy_multiplication_x: | Enable Cloudflare Provider | +| providers.cloudflare.secret.api_key | "" | :heavy_check_mark: | API Key for cloudflare Account | +| providers.cloudflare.secret.email | "" | :heavy_check_mark: | Email for Cloudfalre Account | +| providers.cloudflare.record.name | "" | :heavy_check_mark: | FQDN record *(ex.domain.com)* | +| providers.cloudflare.record.domain | "" | :heavy_check_mark: | DNS Zone *(domain.com)* | +| providers.cloudfalre.record.zone_id | "" | :heavy_check_mark: | ID DNS Zone | + +```yaml +log: + level: debug # Available : trace debug info warn error fatal panic + humanize: true # Default: false + +metrics: + enable: true # Default: false + port: 8080 # Default : 8080 + host: 0.0.0.0 # Default: 0.0.0.0 + path: /metrics # Default: /metrics + +providers: + aws_account: + enable: true + secret: + access_key_id: "xxx" + secret_access_key: "xxx" + region: "eu-west-1" + record: + name: "subdomain.domain.com" + ttl: 60 + domain: "domain.com" + + ovh_account: + enable: true + secret: + application_key: "xxx" + application_secret: "xxx" + consumer_key: "xxx" + region: "eu-west-1" + record: + name: "subdomain.domain.com" + ttl: 60 + zone: "domain.com" + + cloudflare_account: + enable: true + secret: + api_key: "xxx" + email: "xxx" + record: + name: "subdomain.domain.com" + ttl: 60 + domain: "domain.com" +``` \ No newline at end of file diff --git a/docs/setup/docker-compose.md b/docs/setup/docker-compose.md new file mode 100644 index 0000000..25913ca --- /dev/null +++ b/docs/setup/docker-compose.md @@ -0,0 +1,33 @@ +# Docker Compose + +Docker image is stored in [GitHub](https://github.com/users/azrod/packages/container/package/updateip) + +Images is available for Arch : + +* linux/amd64 +* linux/arm +* linux/arm64 + +## Setup compose file + +```bash +version: '3.8' + +services: + updateip: + container_name: updateaip + image: ghcr.io/azrod/updateip:latest + volumes: + - ./config.yaml:/config/config.yaml:ro + environnement: + - LOG_LEVEL=debug + - LOG_HUMANIZE=true + - METRICS_ENABLE=true + - METRICS_PORT=8080 + - METRICS_HOST=0.0.0.0 + port: + - "8080:8080" + restart: always +``` + +[See all available environment variables](envvars.md) \ No newline at end of file diff --git a/docs/setup/docker.md b/docs/setup/docker.md new file mode 100644 index 0000000..7550c79 --- /dev/null +++ b/docs/setup/docker.md @@ -0,0 +1,36 @@ +# Docker + +Docker image is stored in [GitHub](https://github.com/users/azrod/packages/container/package/updateip) + +Images is available for Arch : + +* linux/amd64 +* linux/arm +* linux/arm64 + +## Basic running + +```bash + +docker run -itd \ + -v "./config.yaml:/config/config.yaml" \ + ghcr.io/azrod/updateip:latest + +``` + +## Advanced running + +You can load the configuration via the configuration file and / or by environment variables +[See all available environment variables](envvars.md) + +```bash + +docker run -itd \ + -v "./config.yaml:/config/config.yaml" \ + -e "LOG_LEVEL=debug" \ + -e "LOG_HUMANIZE=true" \ + -e "METRICS_ENABLE=true" \ + -p "8080:8080" \ + ghcr.io/azrod/updateip:latest + +``` diff --git a/docs/setup/envvars.md b/docs/setup/envvars.md new file mode 100644 index 0000000..bfe6fd8 --- /dev/null +++ b/docs/setup/envvars.md @@ -0,0 +1,39 @@ +# Environnement variables + +All parameters can be overloaded by environment variables + +| Options | Actions | +| ------------------------- | --------------------------------------------------------------- | +| PATH_CONFIG_DIRECTORY | Directory where the configuration file is located | +| PATH_CONFIG_FILE | Configuration file name | +| | | +| LOG_LEVEL | Set Log Level *(accept trace, debug, info, warn, error, fatal)* | +| LOG_HUMANIZE | Set human log format | +| | | +| METRICS_ENABLE | Define if start metrics web server | +| METRICS_HOST | Set IP address for metrics web server | +| METRICS_PORT | Set port for metrics web server | +| METRICS_PATH | Path for acceding to metrics web server | +| | | +| AWS_ACCOUNT_ENABLE | Enable AWS Route 53 Provider | +| AWS_ACCESS_KEY_ID | AccessKey for AWS Account | +| AWS_SECRET_ACCESS_KEY | SecretKey for AWS Account | +| AWS_REGION | Region for your domain | +| AWS_RECORD_NAME | FQDN record *(ex.domain.com)* | +| AWS_RECORD_DOMAIN | Domain Name *(domain.com)* | +| AWS_HOSTED_ZONE_ID | HostedZoneID of your domain | +| | | +| OVH_ACCOUNT_ENABLE | Enable OVH Provider | +| OVH_APPLICATION_KEY | Application Key for OVH Account | +| OVH_APPLICATION_SECRET | Application Secret for OVH Account | +| OVH_CONSUMER_KEY | Consumer Key for OVH Account | +| OVH_REGION | Region for your domain | +| OVH_RECORD_NAME | FQDN record *(ex.domain.com)* | +| OVH_RECORD_ZONE | DNS Zone *(domain.com)* | +| | | +| CLOUDFLARE_ACCOUNT_ENABLE | Enable Cloudflare Provider | +| CLOUDFLARE_API_KEY | API Key for cloudflare Account | +| CLOUDFLARE_EMAIL | Email for Cloudfalre Account | +| CLOUDFLARE_RECORD_NAME | FQDN record *(ex.domain.com)* | +| CLOUDFLARE_RECORD_DOMAIN | DNS Zone *(domain.com)* | +| CLOUDFLARE_RECORD_ZONEID | ID DNS Zone | diff --git a/docs/setup/source.md b/docs/setup/source.md new file mode 100644 index 0000000..0b17d41 --- /dev/null +++ b/docs/setup/source.md @@ -0,0 +1,16 @@ +# Running UpdateIP from source + +UpdateIP is develloped in golang and can be executed from source. **Golang 1.17 or later is required**. + +```bash + +cd /tmp/ +git clone git@github.com:azrod/updateip.git && cd updateip + +go mod download +go build + +PATH_CONFIG_DIRECTORY=$(pwd) ./updateip +``` + +[See all available environment variables](envvars.md) \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..3e809e7 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,50 @@ +site_name: UpdateIP + +theme: + name: material + palette: + - scheme: slate + toggle: + icon: material/weather-sunny + name: Switch to light mode + - scheme: default + toggle: + icon: material/weather-night + name: Switch to dark mode + features: + - navigation.sections + - navigation.top + - content.code.annotate + icon: + repo: fontawesome/brands/github-alt + +nav: + - Overview: "index.md" + - Getting Started: "getting-started.md" + - Setup: + - Docker: "setup/docker.md" + - Docker Compose: "setup/docker.md" + - Source: "setup/source.md" + - Config File: "setup/configfile.md" + - Env vars: "setup/envvars.md" + + - Providers: + - AWS Route 53: "providers/aws.md" + - OVH: "providers/ovh.md" + - Cloudflare: "providers/cloudflare.md" + + - Advanced: + - DNS: "advanced/dns.md" + +plugins: + - search + +markdown_extensions: + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + - pymdownx.highlight: + anchor_linenums: true + +repo_url: https://github.com/azrod/updateip +repo_name: azrod/updateip From e1a6a14a34bc1113b77e6f304b69dcd70a49a112 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Sat, 1 Jan 2022 19:30:54 +0100 Subject: [PATCH 4/6] ADD More metrics to AWS provider --- pkg/metrics/metrics.go | 4 ++++ pkg/providers/aws/aws.go | 7 +++++++ pkg/providers/aws/metrics.go | 21 ++++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 33ce911..506fd36 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -62,6 +62,10 @@ func (m *Metrics) RegisterPkg(rg map[string][]interface{}) { for _, v := range rg["counter"] { m.registry.MustRegister(v.(prometheus.Counter)) } + + for _, v := range rg["gaugeVec"] { + m.registry.MustRegister(v.(*prometheus.HistogramVec)) + } } func (m *Metrics) hTTPServer() { diff --git a/pkg/providers/aws/aws.go b/pkg/providers/aws/aws.go index c20ea61..d90b92a 100644 --- a/pkg/providers/aws/aws.go +++ b/pkg/providers/aws/aws.go @@ -24,6 +24,8 @@ var ( ) func (d *Paws) NewClient() error { + defer timeTrackS(time.Now(), "aws_NewClient") + var err error d.clients.aws, err = session.NewSession(&aws.Config{ Region: aws.String(d.Secret.Region), @@ -44,6 +46,7 @@ func (d *Paws) NewClient() error { } func (d *Paws) UpdateRecord(ip net.IP) error { + defer timeTrackS(time.Now(), "aws_UpdateRecord") input := &route53.ChangeResourceRecordSetsInput{ ChangeBatch: &route53.ChangeBatch{ @@ -78,6 +81,7 @@ func (d *Paws) UpdateRecord(ip net.IP) error { } func (d *Paws) getHostedZoneID() (HostedZoneID string, err error) { + defer timeTrackS(time.Now(), "aws_getHostedZoneID") listParams := &route53.ListHostedZonesByNameInput{ DNSName: aws.String(d.Record.Domain), // Required @@ -102,6 +106,7 @@ func (d *Paws) getHostedZoneID() (HostedZoneID string, err error) { } func (d *Paws) GetRecord() (record string, err error) { + defer timeTrackS(time.Now(), "aws_GetRecord") if rec.Expire.After(time.Now()) || rec.LastValue == "" { @@ -126,6 +131,7 @@ func (d *Paws) GetRecord() (record string, err error) { // get change status route53 func (d *Paws) GetChangeStatus() (terminated bool, err error) { + defer timeTrackS(time.Now(), "aws_GetChangeStatus") if rec.LastChangeID != "" { @@ -181,6 +187,7 @@ func (d *Paws) Run() error { if r != i.String() { // go lock() log.Info().Str("DNSIP", r).Str("ActualIP", i.String()).Msg("New IP address detected. Update") + countUpdate.Inc() if err = d.UpdateRecord(i); err != nil { log.Error().Err(err).Msg("Failed to update dns record") } diff --git a/pkg/providers/aws/metrics.go b/pkg/providers/aws/metrics.go index 59e0870..9fa2c16 100644 --- a/pkg/providers/aws/metrics.go +++ b/pkg/providers/aws/metrics.go @@ -1,6 +1,10 @@ package uip_aws -import "github.com/prometheus/client_golang/prometheus" +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" +) var ( countUpdate = prometheus.NewCounter( @@ -17,6 +21,14 @@ var ( Help: "AWS Providers status.", }, ) + + // Histo ... + funcTime = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "updateip_aws_func_time", + Help: "Time taken to do ...", + }, + []string{"where"}, + ) ) func (d *Paws) RegistryMetrics() map[string][]interface{} { @@ -24,6 +36,13 @@ func (d *Paws) RegistryMetrics() map[string][]interface{} { x := make(map[string][]interface{}) x["counter"] = []interface{}{countUpdate} x["gauge"] = []interface{}{providerStatus} + x["gaugeVec"] = []interface{}{funcTime} return x } + +// TimeTrackS ... +func timeTrackS(start time.Time, name string) { + elapsed := time.Since(start) + funcTime.WithLabelValues(name).Observe(elapsed.Seconds()) +} From f5753539363a4957a6487c03bb982542de7abddd Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Sat, 1 Jan 2022 19:32:31 +0100 Subject: [PATCH 5/6] Fix Empty workfow gh action --- .github/workflows/page.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .github/workflows/page.yml diff --git a/.github/workflows/page.yml b/.github/workflows/page.yml deleted file mode 100644 index e69de29..0000000 From bf34a7e7c23e92f10d2651516f36f33d6db05459 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Sat, 1 Jan 2022 19:33:00 +0100 Subject: [PATCH 6/6] Fix Dev gh action --- .github/workflows/dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev.yaml b/.github/workflows/dev.yaml index f906837..69e9e10 100644 --- a/.github/workflows/dev.yaml +++ b/.github/workflows/dev.yaml @@ -4,7 +4,7 @@ name: build on push dev on: push: branches: - - "dev" + - "devel" jobs: build: