goauthentik / goauthentik/authentik
Service Account authentication using AWS presigned sts urls
- Dominant language
- Python
- Stars
- 25.6k
- Forks
- 2k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 644
Description
### Is your feature request related to a problem?
I have ECS and Lambda services running in aws, they run with a least privilege IAM role. AWS provides infrastructure to allow these services to obtain short lived access keys for accessing aws services - i don't have to manage any secrets.
However these services need to call each other, IAM doesn't really have a good answer for this so we turn to an idp to issue a JWT. Authentik supports this, but a service account results in a secret to manage. I don't want to have to manage secrets.
It would be ideal if authentik could authenticate using IAM instead of a user/pass (ie secret token) to authenticate.
### Describe the solution you'd like
Instead of [basic auth](https://docs.goauthentik.io/sys-mgmt/service-accounts/#authentication-with-service-accounts) to obtain a service account jwt from authentik, make a new custom scheme that uses signed sts urls.
It is possible to obtain a signed url for the command `aws sts get-caller-identity` which is the aws equivalent of `whoami`. This URL much like the s3 signed urls many people may be more familiar with exposes no secret information, but does allow an unauthenticated user to call the url and access the resource for a limited time.
Demonstration:
```python
import boto3
import botocore.auth
import botocore.awsrequest
import subprocess
creds = boto3.Session().get_credentials().get_frozen_credentials()
request = botocore.awsrequest.AWSRequest(
method="GET",
url="https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15",
)
botocore.auth.SigV4QueryAuth(creds, "sts", "us-east-1", expires=10).add_auth(request)
print(request.url)
```
The url this produces looks like this:
`https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAZOJT...%2Fus-east-1%2Fsts%2Faws4_request&X-Amz-Date=20260608T000741Z&X-Amz-Expires=10&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2...&X-Amz-Signature=e33fa...592a206def75f90a`
and when accessed returns a xml payload like this:
```xml
arn:aws:sts::123456789012:assumed-role/rolename/sessionname
AROAxxxx:xxxx
123456789012
36935302-4410-4d9a-8834-2ee5755b88a0
```
now you have a sts session arn, which can be trivially turned back into a role ARN with some string manipulation. the role name can be mapped to the principal that is being authenticated, and you have proof that the caller is who they say they are, without any secrets.
Security considerations:
* validate the url as bing a aws sts server (regexes must be anchored)
* replay attack (expires should be short, but otherwise better for replay that user:pass)
* confused deputy, services may sign a request not understanding that it could be used for authentication
* it's possible to add additional arguments in the url that sts includes in the signature but ignores otherwise which could be useful to mitigate confused deputy `purpose=authentik` perhaps
This solves needing secrets for machine accounts running in aws. I'm not familiar enough with other cloud environments to say if there is parallels that would work for azure/gcp.
This does need some additional config mapping on the service account
* disallow password auth when IAM auth is enabled
* store and manage mapping of IAM role ARNs that are allowed to authenticate to service account
Maybe something like:
```
Authorization: AwsAttested
```
### Describe alternatives that you've considered
secrets are ok, but its nice not to have them
### Additional context
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.