mirror of
https://github.com/fatedier/frp.git
synced 2026-05-15 08:05:49 -06:00
[GH-ISSUE #5045] [Feature Request] Auth method: CLI #3969
Labels
No labels
In Progress
WIP
WaitingForInfo
bug
doc
duplicate
easy
enhancement
future
help wanted
invalid
lifecycle/stale
need-issue-template
need-usage-help
no plan
proposal
pull-request
question
todo
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: github-starred/frp#3969
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @Saancreed on GitHub (Nov 3, 2025).
Original GitHub issue: https://github.com/fatedier/frp/issues/5045
Describe the feature request
On the client side, we have a need to be able to fetch PrivilegeKey from another (and often simple) program running on the same machine as the client. The idea would be to add a new, client-only auth method, let's call it
cli, that runs an arbitrary program with given arguments, captures its standard output (stripping it of any trailing newlines), and uses it as the key. The credential retrieved this way could be used with eithertokenoroidcauth method on the server side, depending on whether this program prints a pre-shared key or issues an access token.For example:
or
Optionally, we could consider supporting running the subprocess with additional environment variables but I wouldn't consider that a priority; on UNIX-like systems it's already easy enough to prepend something like
"env", "AZURE_CONFIG_DIR=/etc/azure"tocommand.For simplicity, we could assume that any non-zero exit code from the launched subprocess is an error, and that the output will already be properly (UTF-8?) encoded.
Describe alternatives you've considered
Explicit support for desired credential providers directly in frp
Some sort of plugin system, inspired by RPC-based server plugin system?
Affected area
@fatedier commented on GitHub (Nov 4, 2025):
I’m not inclined to use auth.method = "cli". This configuration is unclear and doesn’t explicitly specify which authentication method is actually being used.
https://github.com/fatedier/frp/blob/dev/pkg/config/v1/value_source.go
We already have the definition of
ValueSource, which currently only supports reading from a file. It might be possible to extend it to support fetching values viaexec.However, I’m a bit concerned that providing the
execoption could introduce security risks. Some users’ configuration files might be loaded from remote sources. Would it be safer to limit support to reading only from files?@Saancreed commented on GitHub (Nov 4, 2025):
Well, it's just an initial proposal of how I thought this feature could be shaped. I actually considered this to be an upside, sort of. When using such mechanism, it becomes more or less irrelevant how the acquired
PrivilegeKeywill be interpreted by the server; in a way,methodon the client side becomes more of a "method of credential acquisition" than "method of authentication".But if you disagree, this "custom credential source" option can be moved into
tokenoroidcmethods themselves. I don't see much value in it, as (if I correctly understand how frp works), with such credential source it doesn't matter on the client side what is configured as top levelauth.method. It does make the intent clear, but slightly obfuscates what is relevant and what isn't.Hmm, that would be… limiting, but perhaps sufficient? It would work if all we wanted was reading the value of pre-shared
token, but it gets trickier if it's expiring access token that has to be periodically regenerated. In order to keep this "on demand", we'd probably have to read from a named pipe that's only written to when frpc accesses it… which I'm not sure how possible or portable it would be.On the other hand, if we drop the "on demand" part, another process could periodically write the access token to a plain file and replace it before it expires, but that mostly makes things like audit logs whenever a token is issued have little to no value.
I'd consider that to already be concering. As far as I can tell, if one launches frpc with a config from untrusted source, it can already extract any secret stored in an environment variable and submit it to an external, attacker-controlled server. If we allowed reading from files, we'd be adding the ability to also send contents of, let's say,
~/.ssh/*.I'm not claiming that ability to launch any process can't be more harmful, because of course it can be, but even reading from files should be something reserved for trusted configurations. At which point, if this mechanism becomes explicitly opt-in, via explicit
--allow-file-auth-methodflag perhaps, why not go all the way in and make it into--allow-cli-auth-method?@fatedier commented on GitHub (Nov 5, 2025):
Overall, I’m inclined to support this feature. Using a flag to control whether external commands are allowed to execute is a good idea — it must be explicitly enabled by the user and cannot be disabled through configuration files.
The specific implementation plan still needs further discussion.
@Saancreed commented on GitHub (Nov 5, 2025):
Wonderful, thank you. Having some
cli/execoption together with a flag required to enable it sounds good for us.The naming of the config option and the flag itself isn't too important, we'll just adapt to your preferences. Once they are confirmed, we can contribute initial implementation (with documentation updates and tests) if it would be helpful. Let me know how you'd like to see this done and I'll be able to start working on some pull requests.
@fatedier commented on GitHub (Nov 6, 2025):
Are there any other widely used projects that provide similar functionality? How are they designed?
@Saancreed commented on GitHub (Nov 6, 2025):
There are a few I'm aware of, and likely many more that I'm not aware of. Perhaps the best example would be
kubectl, which allows executing any program to provide client credentials used to authenticate to Kubernetes clusters: https://kubernetes.io/docs/reference/config-api/kubeconfig.v1/#ExecConfigThis is used by Azure CLI, which when asked to connect to Azure Kubernetes Service with
az aks get-credentials, creates a kubeconfig entry for cluster credentials that looks like this:Other examples (not necessarily used for producing credentials) would be:
credential_processsetting: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sourcing-external.htmlexternaldata source: https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/externallocalfunction: https://docs.tilt.dev/api.html#api.localFinally, some programs like Git and Docker have similar integrations via Credential Helpers but they aren't as flexible and impose somewhat greater restrictions on what invoked program has to do:
Honorary mention goes to
opensshandsudothat can run an askpass program to retrieve the user's passphrase instead of asking the user directly.Based on all this, and considering
kubectlto be the closest equivalent to what we want to achieve, I'd suggest that:kubectlsplitscommandandargsinto separate entries, it seems to be an unnecessary complication; a single array that contains both of them concatenated would be simplerAs a side note, and as I mentioned in the initial proposal, there's probably no need to require any particular output format; just taking the entire stdout and stripping trailing newlines, if any, should be good enough.
@fatedier commented on GitHub (Nov 7, 2025):
Because of historical iterations in the project, the OIDC access token is also carried via the
PrivilegeKeyfield in the Login message. However, howPrivilegeKeyis set and parsed depends on the selectedauth.method. Whenmethod = "token",auth.tokenis MD5-hashed before being sent; whenmethod = "oidc", the value is used as-is.That means the example you provided would fail:
The reason is that
frpcisn’t usingauth.method = "token", so it doesn’t know it must apply the token flow (MD5) before placing the value intoPrivilegeKey.Short term, as mentioned earlier, we could extend
ValueSourceto supportexec(with dynamic refresh), and makeauth.oidcaccept the same extension fields.Longer term, we probably need to revisit this part of the handshake/protocol. Instead of overloading
PrivilegeKey, we should send a more structured credential payload. That would also make it possible forfrpsto support multiple authentication mechanisms concurrently in the future.@Saancreed commented on GitHub (Nov 7, 2025):
Ah, I see, the distinction is more important than I thought. In that case we could go with something like
Not sure if the command should be inNevermind, I looked more closely at howexecsection or somewhere else though. I suppose there's little reason to support multiple authentication methods on the client side concurrently? Then we wouldn't have to worry about conflicts.ValueSourceis designed and I just mirroredtokenSourcebut foroidcmethod. But maybe we should reusetokenSourceeven ifmethod = "oidc"?By dynamic refresh, do you mean
ValueSource(or something else) checking if the token expired (or is about to), and only then run the command again? It could be a nice improvement for heartbeats, but maybe we could initially make it the command's responsibility to handle caches, if any, on its side.Anyway, I think this is enough information for me to start prototyping something on top of
ValueSource…@fatedier commented on GitHub (Nov 10, 2025):
I prefer to use the name
tokenSourceforoidcmethod.In addition, we should refer to the design in kubeconfig and keep the
command,args, andenvfields, so that it aligns better with common user practices.@Saancreed commented on GitHub (Nov 10, 2025):
Sure, I went with that. Minor concern about this choice though: what if some users already have configs that look like
and only use
auth.methodto toggle between the two? Probably not likely to happen in practice as the server currently supports only one authentication method, so perhaps it would be a good idea to maketokenSourcealso mutually exclusive withoidcsection, the same way it currently is mutually exclusive withtokento avoid ambiguities.Okay, not a problem. I'm not sure if
envshould be also copied as-is fromkubeconfig(where it's an array of structs withnameandvaluemembers), what I currently have is a simple map and that appears to be working fine. With this, the current design looks like so:@Saancreed commented on GitHub (Nov 24, 2025):
@fatedier Now that related PR with the feature itself is merged, would you like us to also contribute some tests and documentation for this feature?
@fatedier commented on GitHub (Nov 25, 2025):
@Saancreed There are still some issues that need to be adjusted and fixed. I’ll make some modifications and improvements myself.