From 5043f157faa2cb1e7828fc30467420a4a0c36b0c Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sat, 5 Aug 2023 00:38:21 -0500 Subject: [PATCH] Add support for using cookie for auth (#205) --- .gitignore | 1 + README.md | 43 +++++++++++++++++++++++++++++-------------- cmd/root.go | 5 +++++ cmd/run.go | 4 ++++ pkg/client/client.go | 9 ++++++++- pkg/types/types.go | 1 + 6 files changed, 48 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index e4f6e9b..8a70631 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vscode .idea coverage.out dist diff --git a/README.md b/README.md index caae9e1..198fbac 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,13 @@ go install github.com/bakito/adguardhome-sync@latest Both the origin instance must be initially setup via the AdguardHome installation wizard. +## Username / Password vs. Cookie + +Some instances of AdGuard Home do not support basic authentication. For instance, many routers with built-in Adguard Home support do not. If this is the case, a valid cookie may be provided instead. If the router protects the AdGuard instance behind its own authentication, the cookie from an authenticated request may allow the sync to succeed. + +- This has been tested successfully against GL.Inet routers with AdGuard Home. +- Note: due to the short validity of cookies, this approach is likely only suitable for one-time syncs + ## Run Linux/Mac ```bash @@ -45,9 +52,11 @@ export LOG_LEVEL=info export ORIGIN_URL=https://192.168.1.2:3000 export ORIGIN_USERNAME=username export ORIGIN_PASSWORD=password +# export ORIGIN_COOKIE=Origin-Cookie-Name=CCCOOOKKKIIIEEE export REPLICA_URL=http://192.168.1.3 export REPLICA_USERNAME=username export REPLICA_PASSWORD=password +# export REPLICA_COOKIE=Replica-Cookie-Name=CCCOOOKKKIIIEEE # run once adguardhome-sync run @@ -70,10 +79,12 @@ REM set LOG_LEVEL=error set ORIGIN_URL=http://192.168.1.2:3000 set ORIGIN_USERNAME=username set ORIGIN_PASSWORD=password +# set ORIGIN_COOKIE=Origin-Cookie-Name=CCCOOOKKKIIIEEE set REPLICA_URL=http://192.168.2.2:3000 set REPLICA_USERNAME=username set REPLICA_PASSWORD=password +# set REPLICA_COOKIE=Replica-Cookie-Name=CCCOOOKKKIIIEEE set FEATURES_DHCP=false set FEATURES_DHCP_SERVERCONFIG=false @@ -127,21 +138,21 @@ services: container_name: adguardhome-sync command: run environment: - LOG_LEVEL: 'info' - ORIGIN_URL: 'https://192.168.1.2:3000' - ORIGIN_USERNAME: 'username' - ORIGIN_PASSWORD: 'password' - REPLICA_URL: 'http://192.168.1.3' - REPLICA_USERNAME: 'username' - REPLICA_PASSWORD: 'password' - REPLICA1_URL: 'http://192.168.1.4' - REPLICA1_USERNAME: 'username' - REPLICA1_PASSWORD: 'password' - REPLICA1_APIPATH: '/some/path/control' + LOG_LEVEL: "info" + ORIGIN_URL: "https://192.168.1.2:3000" + ORIGIN_USERNAME: "username" + ORIGIN_PASSWORD: "password" + REPLICA_URL: "http://192.168.1.3" + REPLICA_USERNAME: "username" + REPLICA_PASSWORD: "password" + REPLICA1_URL: "http://192.168.1.4" + REPLICA1_USERNAME: "username" + REPLICA1_PASSWORD: "password" + REPLICA1_APIPATH: "/some/path/control" # REPLICA1_AUTOSETUP: true # if true, AdGuardHome is automatically initialized. # REPLICA1_INTERFACENAME: 'ens18' # use custom dhcp interface name # REPLICA1_DHCPSERVERENABLED: true/false (optional) enables/disables the dhcp server on the replica - CRON: '*/10 * * * *' # run every 10 minutes + CRON: "*/10 * * * *" # run every 10 minutes RUNONSTART: true # Configure the sync API server, disabled if api port is 0 @@ -182,6 +193,7 @@ origin: # insecureSkipVerify: true # disable tls check username: username password: password + # cookie: Origin-Cookie-Name=CCCOOOKKKIIIEEE # replica instance (optional, if only one) replica: @@ -189,6 +201,7 @@ replica: url: http://192.168.1.3 username: username password: password + # cookie: Replica-Cookie-Name=CCCOOOKKKIIIEEE # replicas instances (optional, if more than one) replicas: @@ -196,16 +209,18 @@ replicas: - url: http://192.168.1.3 username: username password: password + # cookie: Replica1-Cookie-Name=CCCOOOKKKIIIEEE - url: http://192.168.1.4 username: username password: password - # autoSetup: true # if true, AdGuardHome is automatically initialized. + # cookie: Replica2-Cookie-Name=CCCOOOKKKIIIEEE + # autoSetup: true # if true, AdGuardHome is automatically initialized. # Configure the sync API server, disabled if api port is 0 api: # Port, default 8080 port: 8080 - # if username and password are defined, basic auth is applied to the sync API + # if username and password are defined, basic auth is applied to the sync API username: username password: password diff --git a/cmd/root.go b/cmd/root.go index 26c97d7..57c32cb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -40,18 +40,21 @@ const ( configOriginAPIPath = "origin.apiPath" configOriginUsername = "origin.username" configOriginPassword = "origin.password" + configOriginCookie = "origin.cookie" configOriginInsecureSkipVerify = "origin.insecureSkipVerify" configReplicaURL = "replica.url" configReplicaAPIPath = "replica.apiPath" configReplicaUsername = "replica.username" configReplicaPassword = "replica.password" + configReplicaCookie = "replica.cookie" configReplicaInsecureSkipVerify = "replica.insecureSkipVerify" configReplicaAutoSetup = "replica.autoSetup" configReplicaInterfaceName = "replica.interfaceName" envReplicasUsernameFormat = "REPLICA%s_USERNAME" // #nosec G101 envReplicasPasswordFormat = "REPLICA%s_PASSWORD" // #nosec G101 + envReplicasCookieFormat = "REPLICA%s_COOKIE" // #nosec G101 envReplicasAPIPathFormat = "REPLICA%s_APIPATH" envReplicasInsecureSkipVerifyFormat = "REPLICA%s_INSECURESKIPVERIFY" envReplicasAutoSetup = "REPLICA%s_AUTOSETUP" @@ -148,11 +151,13 @@ func collectEnvReplicas(logger *zap.SugaredLogger) []types.AdGuardInstance { URL: sm[2], Username: os.Getenv(fmt.Sprintf(envReplicasUsernameFormat, sm[1])), Password: os.Getenv(fmt.Sprintf(envReplicasPasswordFormat, sm[1])), + Cookie: os.Getenv(fmt.Sprintf(envReplicasCookieFormat, sm[1])), APIPath: os.Getenv(fmt.Sprintf(envReplicasAPIPathFormat, sm[1])), InsecureSkipVerify: strings.EqualFold(os.Getenv(fmt.Sprintf(envReplicasInsecureSkipVerifyFormat, sm[1])), "true"), AutoSetup: strings.EqualFold(os.Getenv(fmt.Sprintf(envReplicasAutoSetup, sm[1])), "true"), InterfaceName: os.Getenv(fmt.Sprintf(envReplicasInterfaceName, sm[1])), } + if re.InterfaceName == "" { if in, ok := os.LookupEnv(fmt.Sprintf(envReplicasInterfaceNameDeprecated, sm[1])); ok { logger. diff --git a/cmd/run.go b/cmd/run.go index c1b643a..030aa06 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -72,6 +72,8 @@ func init() { _ = viper.BindPFlag(configOriginUsername, doCmd.PersistentFlags().Lookup("origin-username")) doCmd.PersistentFlags().String("origin-password", "", "Origin instance password") _ = viper.BindPFlag(configOriginPassword, doCmd.PersistentFlags().Lookup("origin-password")) + doCmd.PersistentFlags().String("origin-cookie", "", "If Set, uses a cookie for authentication") + _ = viper.BindPFlag(configOriginCookie, doCmd.PersistentFlags().Lookup("origin-cookie")) doCmd.PersistentFlags().Bool("origin-insecure-skip-verify", false, "Enable Origin instance InsecureSkipVerify") _ = viper.BindPFlag(configOriginInsecureSkipVerify, doCmd.PersistentFlags().Lookup("origin-insecure-skip-verify")) @@ -83,6 +85,8 @@ func init() { _ = viper.BindPFlag(configReplicaUsername, doCmd.PersistentFlags().Lookup("replica-username")) doCmd.PersistentFlags().String("replica-password", "", "Replica instance password") _ = viper.BindPFlag(configReplicaPassword, doCmd.PersistentFlags().Lookup("replica-password")) + doCmd.PersistentFlags().String("replica-cookie", "", "If Set, uses a cookie for authentication") + _ = viper.BindPFlag(configReplicaCookie, doCmd.PersistentFlags().Lookup("replica-cookie")) doCmd.PersistentFlags().Bool("replica-insecure-skip-verify", false, "Enable Replica instance InsecureSkipVerify") _ = viper.BindPFlag(configReplicaInsecureSkipVerify, doCmd.PersistentFlags().Lookup("replica-insecure-skip-verify")) doCmd.PersistentFlags().Bool("replica-auto-setup", false, "Enable automatic setup of new AdguardHome instances. This replaces the setup wizard.") diff --git a/pkg/client/client.go b/pkg/client/client.go index 8b56323..273b65d 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -10,6 +10,7 @@ import ( "os" "path" "strconv" + "strings" "github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/types" @@ -56,7 +57,13 @@ func New(config types.AdGuardInstance) (Client, error) { cl.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) } - if config.Username != "" && config.Password != "" { + cookieParts := strings.Split(config.Cookie, "=") + if len(cookieParts) == 2 { + cl.SetCookie(&http.Cookie{ + Name: cookieParts[0], + Value: cookieParts[1], + }) + } else if config.Username != "" && config.Password != "" { cl = cl.SetBasicAuth(config.Username, config.Password) } diff --git a/pkg/types/types.go b/pkg/types/types.go index e9a721a..8680c78 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -81,6 +81,7 @@ type AdGuardInstance struct { APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty"` Username string `json:"username,omitempty" yaml:"username,omitempty"` Password string `json:"password,omitempty" yaml:"password,omitempty"` + Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty"` InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"` AutoSetup bool `json:"autoSetup" yaml:"autoSetup"` InterfaceName string `json:"interfaceName" yaml:"interfaceName"`