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
|
||||
|
||||
#### 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
|
||||
|
||||
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.Auth.Token, "token", "t", "", "auth token")
|
||||
cmd.PersistentFlags().StringVarP(&c.Auth.TokenFile, "token_file", "", "", "auth token file")
|
||||
}
|
||||
|
||||
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().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.TokenFile, "token_file", "", "", "auth token file")
|
||||
cmd.PersistentFlags().StringVarP(&c.SubDomainHost, "subdomain_host", "", "", "subdomain host")
|
||||
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")
|
||||
|
@ -103,6 +103,15 @@ func LoadFileContentWithTemplate(path string, values *Values) ([]byte, error) {
|
||||
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 {
|
||||
content, err := LoadFileContentWithTemplate(path, GetValues())
|
||||
if err != nil {
|
||||
|
@ -179,12 +179,20 @@ type AuthClientConfig struct {
|
||||
// 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 succeed. By default, this value is "".
|
||||
Token string `json:"token,omitempty"`
|
||||
OIDC AuthOIDCClientConfig `json:"oidc,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
TokenFile string `json:"tokenFile,omitempty"`
|
||||
OIDC AuthOIDCClientConfig `json:"oidc,omitempty"`
|
||||
}
|
||||
|
||||
func (c *AuthClientConfig) Complete() {
|
||||
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 {
|
||||
|
@ -126,11 +126,18 @@ type AuthServerConfig struct {
|
||||
Method AuthMethod `json:"method,omitempty"`
|
||||
AdditionalScopes []AuthScope `json:"additionalScopes,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
TokenFile string `json:"tokenFile,omitempty"`
|
||||
OIDC AuthOIDCServerConfig `json:"oidc,omitempty"`
|
||||
}
|
||||
|
||||
func (c *AuthServerConfig) Complete() {
|
||||
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 {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
mathrand "math/rand/v2"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -134,3 +135,12 @@ func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Durati
|
||||
func ConstantTimeEqString(a, b string) bool {
|
||||
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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -149,6 +150,64 @@ var _ = ginkgo.Describe("[Feature: Client-Server]", func() {
|
||||
client: `auth.token = "invalid"`,
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user