Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

About forwardproxy configuration multi-user #248

Closed
ghost opened this issue Dec 20, 2021 · 7 comments
Closed

About forwardproxy configuration multi-user #248

ghost opened this issue Dec 20, 2021 · 7 comments

Comments

@ghost
Copy link

ghost commented Dec 20, 2021

Due to special reasons, it may be necessary to configure multiple basic_auths on forwardproxy for some businesses. At present, I have found that the author's caddyserver/forwardproxy#81 has a description.
Since I am not an expert in go, I am just a front-end programmer. But I want to provide my own ideas, and I hope to discuss with the author, but as for the code implementation, the author may need to complete it.

I want to implement a similar configuration for Caddyfile:

{
    servers :443 {
      protocol {
        experimental_http3
      }
    }
}
:443, your-domain-name.com
route {
    forward_proxy {
      basic_auth user1 pass1
      basic_auth user2 pass2
      hide_ip
      hide_via
      probe_resistance
    }
}

Although I am not good at the go language, I looked through the caddy source code and found that basicauth.go can support multiple user configurations. https://github.com/caddyserver/caddy/blob/03b5debd958a324d5c00cc37f887130b0198e747/modules/caddyhttp/caddyauth/basicauth.go#L106

It made me out of a bud, so I simply implemented it based on basicauth.go through the go language. The idea is as follows:

package main

import (
	"crypto/subtle"
	"fmt"
)
import "encoding/base64"

// HTTPBasicAuth facilitates HTTP basic authentication.
type HTTPBasicAuth struct {
	// The list of accounts to authenticate.
	AccountList []Account          `json:"accounts,omitempty"`
	Accounts    map[string]Account `json:"-"`
}

// Account contains a username, password, and salt (if applicable).
type Account struct {
	// TODO: temporary/deprecated - we should try to reuse existing authentication modules instead!
	BasicauthUser   string `json:"auth_user_deprecated,omitempty"`
	BasicauthPass   string `json:"auth_pass_deprecated,omitempty"`
	authRequired    bool
	authCredentials [][]byte // slice with base64-encoded credentials
}

func main() {
	var basic_auth HTTPBasicAuth
	basic_auth.AccountList = append(basic_auth.AccountList, Account{BasicauthUser: "zhangsan", BasicauthPass: "123456"})
	basic_auth.AccountList = append(basic_auth.AccountList, Account{BasicauthUser: "lisi", BasicauthPass: "123456"})

	basic_auth.Accounts = map[string]Account{}
	for i, acct := range basic_auth.AccountList {
		if acct.BasicauthUser == "" || acct.BasicauthPass == "" {
			fmt.Errorf("account %d: username and password are required", i)
		}
		//log.Printf("%s:%s", acct.BasicauthUser, acct.BasicauthPass)

		basicAuthBuf := make([]byte, base64.StdEncoding.EncodedLen(len(acct.BasicauthUser)+1+len(acct.BasicauthPass)))
		base64.StdEncoding.Encode(basicAuthBuf, []byte(acct.BasicauthUser+":"+acct.BasicauthPass))
		acct.authRequired = true
		acct.authCredentials = [][]byte{basicAuthBuf}

		basic_auth.Accounts[acct.BasicauthUser] = acct
	}

	//fmt.Println(basic_auth.AccountList)
	//fmt.Println(basic_auth.Accounts)

	// Simulate the Proxy-Authorization sent from the client
	for _, acct := range basic_auth.Accounts {
		//fmt.Println(acct.authCredentials)
		for _, creds := range acct.authCredentials {
			if subtle.ConstantTimeCompare(creds, []byte("emhhbmdzYW46MTIzNDU2")) == 1 {
				fmt.Println("zhangsan:123456 User authentication succeeded") // zhangsan:123456
			}

			if subtle.ConstantTimeCompare(creds, []byte("bGlzaToxMjM0NTY=")) == 1 {
				fmt.Println("lisi:123456 User authentication succeeded") // lisi:123456
			}
		}
	}
}

The final output is:

zhangsan:123456 User authentication succeeded
lisi:123456 User authentication succeeded

And I found that forwardproxy.go contains the checkCredentials method, this is whether I can use multi-threaded authentication to authenticate users (specific difficulties need to be realized by the author, only to provide ideas):

func (h Handler) checkCredentials(r *http.Request) error {
	pa := strings.Split(r.Header.Get("Proxy-Authorization"), " ")
	if len(pa) != 2 {
		return errors.New("Proxy-Authorization is required! Expected format: <type> <credentials>")
	}
	if strings.ToLower(pa[0]) != "basic" {
		return errors.New("Auth type is not supported")
	}
	for _, creds := range h.authCredentials {
		if subtle.ConstantTimeCompare(creds, []byte(pa[1])) == 1 {
			// Please do not consider this to be timing-attack-safe code. Simple equality is almost
			// mindlessly substituted with constant time algo and there ARE known issues with this code,
			// e.g. size of smallest credentials is guessable. TODO: protect from all the attacks! Hash?
			return nil
		}
	}
	return errors.New("Invalid credentials")
}
@klzgrad
Copy link
Owner

klzgrad commented Dec 21, 2021

If you have created a working patch you can submit it to upstream and discuss the patch there.

This part is not strictly related to naiveproxy and should be discussed upstream.

@ghost
Copy link
Author

ghost commented Dec 21, 2021

If you have created a working patch you can submit it to upstream and discuss the patch there.

This part is not strictly related to naiveproxy and should be discussed upstream.

I have not implemented it yet, because I am not familiar with the go language, and I am afraid that implementing multiple users may affect the security of forwardproxy, so I wonder if you can implement it?

@ghost
Copy link
Author

ghost commented Dec 21, 2021

At present, I only provide ideas, and further implementation may require the author to consider and review whether this feature can be added. But I still hope to add it.

@ghost
Copy link
Author

ghost commented Dec 21, 2021

If you have created a working patch you can submit it to upstream and discuss the patch there.

This part is not strictly related to naiveproxy and should be discussed upstream.

One question I would like to ask is, once naive establishes a tls connection with the proxy server, every request of the proxy will automatically carry "Proxy-Authorization" for authentication?

@klzgrad
Copy link
Owner

klzgrad commented Dec 21, 2021

Multi user authentication would require the necessary next step of multi user accounting and the whole AAA management stuff, which is not the focus of this project. The main technical problem is how to do multi user authentication in constant time to prevent side channels.

It does, but does so with 1 byte due to HPACK compression.

@klzgrad klzgrad closed this as completed Dec 21, 2021
@klzgrad
Copy link
Owner

klzgrad commented Dec 21, 2021

It's possible to have multiple users with https://github.com/klzgrad/naiveproxy/wiki/HAProxy-Setup userlist in Haproxy.

@ghost
Copy link
Author

ghost commented Dec 22, 2021

It's possible to have multiple users with https://github.com/klzgrad/naiveproxy/wiki/HAProxy-Setup userlist in Haproxy.

However, I read the source code of forwardproxy's caddy1 version and found that there is indeed a multi-user implementation, and the authentication information of multiple users is stored in the form of authCredentials [][]bytehttps://github.com/klzgrad/forwardproxy/blob/247c0bafaabd39e17ecf82c2c957c46957c2efcc/forwardproxy.go#L44 https://github.com/klzgrad/forwardproxy/blob/247c0bafaabd39e17ecf82c2c957c46957c2efcc/setup.go#L75. But I don’t understand why the caddy2 version does not use the forwardproxy method of the caddy1 version. Is it not safe enough or is it? At the same time, I found that basic_auth authentication is based on base64, and base64 is currently too easy to encrypt and decrypt. If the protection of TLS is removed, it is completely exposed. Is there any further security considerations?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant