feat: Add auth.tokenFile support for file-based token authentication
This commit is contained in:
parent
03c8d7bf96
commit
b2a468aa55
27
README.md
27
README.md
@ -603,6 +603,33 @@ When specifying `auth.method = "token"` in `frpc.toml` and `frps.toml` - token b
|
|||||||
|
|
||||||
Make sure to specify the same `auth.token` in `frps.toml` and `frpc.toml` for frpc to pass frps validation
|
Make sure to specify the same `auth.token` in `frps.toml` and `frpc.toml` for frpc to pass frps validation
|
||||||
|
|
||||||
|
#### File-based Token Authentication
|
||||||
|
|
||||||
|
If you prefer not to include the token directly in the configuration file, you can specify the path to a file containing the token using the `auth.tokenFile` option.
|
||||||
|
|
||||||
|
Instead of setting `auth.token`, specify the path to the file containing your token with `auth.tokenFile`.
|
||||||
|
Ensure that the file contains only the token and no additional content. Example configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# frpc.toml
|
||||||
|
[common]
|
||||||
|
auth.tokenFile = "/path/to/your/token_file"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# frps.toml
|
||||||
|
[common]
|
||||||
|
auth.tokenFile = "/path/to/your/token_file"
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure the file at the specified path is readable by the application and contains the following:
|
||||||
|
|
||||||
|
``` txt
|
||||||
|
your_secret_token
|
||||||
|
```
|
||||||
|
|
||||||
|
**Notes:** If both `auth.token` and `auth.tokenFile` are specified, the token from `auth.token` will be used.
|
||||||
|
|
||||||
#### OIDC Authentication
|
#### OIDC Authentication
|
||||||
|
|
||||||
When specifying `auth.method = "oidc"` in `frpc.toml` and `frps.toml` - OIDC based authentication will be used.
|
When specifying `auth.method = "oidc"` in `frpc.toml` and `frps.toml` - OIDC based authentication will be used.
|
||||||
|
@ -165,6 +165,7 @@ func RegisterClientCommonConfigFlags(cmd *cobra.Command, c *v1.ClientCommonConfi
|
|||||||
}
|
}
|
||||||
cmd.PersistentFlags().StringVarP(&c.User, "user", "u", "", "user")
|
cmd.PersistentFlags().StringVarP(&c.User, "user", "u", "", "user")
|
||||||
cmd.PersistentFlags().StringVarP(&c.Auth.Token, "token", "t", "", "auth token")
|
cmd.PersistentFlags().StringVarP(&c.Auth.Token, "token", "t", "", "auth token")
|
||||||
|
cmd.PersistentFlags().StringVarP(&c.Auth.TokenFile, "token_file", "", "", "auth token file")
|
||||||
}
|
}
|
||||||
|
|
||||||
type PortsRangeSliceFlag struct {
|
type PortsRangeSliceFlag struct {
|
||||||
@ -240,6 +241,7 @@ func RegisterServerConfigFlags(cmd *cobra.Command, c *v1.ServerConfig, opts ...R
|
|||||||
cmd.PersistentFlags().Int64VarP(&c.Log.MaxDays, "log_max_days", "", 3, "log max days")
|
cmd.PersistentFlags().Int64VarP(&c.Log.MaxDays, "log_max_days", "", 3, "log max days")
|
||||||
cmd.PersistentFlags().BoolVarP(&c.Log.DisablePrintColor, "disable_log_color", "", false, "disable log color in console")
|
cmd.PersistentFlags().BoolVarP(&c.Log.DisablePrintColor, "disable_log_color", "", false, "disable log color in console")
|
||||||
cmd.PersistentFlags().StringVarP(&c.Auth.Token, "token", "t", "", "auth token")
|
cmd.PersistentFlags().StringVarP(&c.Auth.Token, "token", "t", "", "auth token")
|
||||||
|
cmd.PersistentFlags().StringVarP(&c.Auth.TokenFile, "token_file", "", "", "auth token file")
|
||||||
cmd.PersistentFlags().StringVarP(&c.SubDomainHost, "subdomain_host", "", "", "subdomain host")
|
cmd.PersistentFlags().StringVarP(&c.SubDomainHost, "subdomain_host", "", "", "subdomain host")
|
||||||
cmd.PersistentFlags().VarP(&PortsRangeSliceFlag{V: &c.AllowPorts}, "allow_ports", "", "allow ports")
|
cmd.PersistentFlags().VarP(&PortsRangeSliceFlag{V: &c.AllowPorts}, "allow_ports", "", "allow ports")
|
||||||
cmd.PersistentFlags().Int64VarP(&c.MaxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
|
cmd.PersistentFlags().Int64VarP(&c.MaxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
|
||||||
|
@ -103,6 +103,15 @@ func LoadFileContentWithTemplate(path string, values *Values) ([]byte, error) {
|
|||||||
return RenderWithTemplate(b, values)
|
return RenderWithTemplate(b, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadFileToken(path string) (string, error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
token := strings.TrimSpace(string(data))
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
func LoadConfigureFromFile(path string, c any, strict bool) error {
|
func LoadConfigureFromFile(path string, c any, strict bool) error {
|
||||||
content, err := LoadFileContentWithTemplate(path, GetValues())
|
content, err := LoadFileContentWithTemplate(path, GetValues())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -179,12 +179,20 @@ type AuthClientConfig struct {
|
|||||||
// Token specifies the authorization token used to create keys to be sent
|
// Token specifies the authorization token used to create keys to be sent
|
||||||
// to the server. The server must have a matching token for authorization
|
// to the server. The server must have a matching token for authorization
|
||||||
// to succeed. By default, this value is "".
|
// to succeed. By default, this value is "".
|
||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
OIDC AuthOIDCClientConfig `json:"oidc,omitempty"`
|
TokenFile string `json:"tokenFile,omitempty"`
|
||||||
|
OIDC AuthOIDCClientConfig `json:"oidc,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AuthClientConfig) Complete() {
|
func (c *AuthClientConfig) Complete() {
|
||||||
c.Method = util.EmptyOr(c.Method, "token")
|
c.Method = util.EmptyOr(c.Method, "token")
|
||||||
|
|
||||||
|
if c.Token == "" && c.TokenFile != "" {
|
||||||
|
tokenFromFile, err := util.LoadFileToken(c.TokenFile)
|
||||||
|
if err == nil {
|
||||||
|
c.Token = tokenFromFile
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthOIDCClientConfig struct {
|
type AuthOIDCClientConfig struct {
|
||||||
|
@ -126,11 +126,18 @@ type AuthServerConfig struct {
|
|||||||
Method AuthMethod `json:"method,omitempty"`
|
Method AuthMethod `json:"method,omitempty"`
|
||||||
AdditionalScopes []AuthScope `json:"additionalScopes,omitempty"`
|
AdditionalScopes []AuthScope `json:"additionalScopes,omitempty"`
|
||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
|
TokenFile string `json:"tokenFile,omitempty"`
|
||||||
OIDC AuthOIDCServerConfig `json:"oidc,omitempty"`
|
OIDC AuthOIDCServerConfig `json:"oidc,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AuthServerConfig) Complete() {
|
func (c *AuthServerConfig) Complete() {
|
||||||
c.Method = util.EmptyOr(c.Method, "token")
|
c.Method = util.EmptyOr(c.Method, "token")
|
||||||
|
if c.Token == "" && c.TokenFile != "" {
|
||||||
|
tokenFromFile, err := util.LoadFileToken(c.TokenFile)
|
||||||
|
if err == nil {
|
||||||
|
c.Token = tokenFromFile
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthOIDCServerConfig struct {
|
type AuthOIDCServerConfig struct {
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
mathrand "math/rand/v2"
|
mathrand "math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -134,3 +135,12 @@ func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Durati
|
|||||||
func ConstantTimeEqString(a, b string) bool {
|
func ConstantTimeEqString(a, b string) bool {
|
||||||
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
|
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadFileToken(path string) (string, error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
token := strings.TrimSpace(string(data))
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package basic
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -149,6 +150,64 @@ var _ = ginkgo.Describe("[Feature: Client-Server]", func() {
|
|||||||
client: `auth.token = "invalid"`,
|
client: `auth.token = "invalid"`,
|
||||||
expectError: true,
|
expectError: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defineClientServerTest("Client Token File Correct", f, &generalTestConfigures{
|
||||||
|
server: `auth.token = "123456"`,
|
||||||
|
client: func() string {
|
||||||
|
tokenFile := f.WriteTempFile("correct_token.txt", "123456")
|
||||||
|
framework.AddCleanupAction(func() {
|
||||||
|
os.Remove(tokenFile)
|
||||||
|
})
|
||||||
|
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
|
||||||
|
}(),
|
||||||
|
})
|
||||||
|
|
||||||
|
defineClientServerTest("Server Client Token File Correct", f, &generalTestConfigures{
|
||||||
|
server: func() string {
|
||||||
|
tokenFile := f.WriteTempFile("correct_token.txt", "123456")
|
||||||
|
framework.AddCleanupAction(func() {
|
||||||
|
os.Remove(tokenFile)
|
||||||
|
})
|
||||||
|
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
|
||||||
|
}(),
|
||||||
|
client: `auth.token = "123456"`,
|
||||||
|
})
|
||||||
|
|
||||||
|
defineClientServerTest("Client Token File Incorrect", f, &generalTestConfigures{
|
||||||
|
server: `auth.token = "123456"`,
|
||||||
|
client: func() string {
|
||||||
|
tokenFile := f.WriteTempFile("incorrect_token.txt", "invalid")
|
||||||
|
framework.AddCleanupAction(func() {
|
||||||
|
os.Remove(tokenFile)
|
||||||
|
})
|
||||||
|
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
|
||||||
|
}(),
|
||||||
|
expectError: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
defineClientServerTest("Server Token File Incorrect", f, &generalTestConfigures{
|
||||||
|
server: func() string {
|
||||||
|
tokenFile := f.WriteTempFile("incorrect_token.txt", "invalid")
|
||||||
|
framework.AddCleanupAction(func() {
|
||||||
|
os.Remove(tokenFile)
|
||||||
|
})
|
||||||
|
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
|
||||||
|
}(),
|
||||||
|
client: `auth.token = "123456"`,
|
||||||
|
expectError: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
defineClientServerTest("Client Token File Not Exist", f, &generalTestConfigures{
|
||||||
|
server: `auth.token = "123456"`,
|
||||||
|
client: `auth.tokenFile = "/path/to/non/existent/file"`,
|
||||||
|
expectError: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
defineClientServerTest("Server Token File Not Exist", f, &generalTestConfigures{
|
||||||
|
server: `auth.tokenFile = "/path/to/non/existent/file"`,
|
||||||
|
client: `auth.token = "123456"`,
|
||||||
|
expectError: true,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.Describe("TLS", func() {
|
ginkgo.Describe("TLS", func() {
|
||||||
|
Loading…
Reference in New Issue
Block a user