Tailscale
This guide explains how to set up single sign-on (SSO) between VaultysHub and Tailscale using OpenID Connect with WebFinger for simplified configuration.
Prerequisites
- Tailscale account with a plan supporting custom SSO (Business or Enterprise)
- Administrator access to Tailscale
- Verified domain in Tailscale
- Application configured in VaultysHub with OpenID Connect
- A WebFinger endpoint served by your email domain (required by Tailscale for custom OIDC)
Overview
Tailscale supports SSO authentication via OpenID Connect with a unique feature: WebFinger. This allows users to log in simply with their email address, without needing to know the provider's URL.
Configuration in VaultysHub
1. Create the application
- Log in to VaultysHub as an administrator
- Go to Applications → Add
- Create a new application:
- Name: Tailscale
- URL:
https://login.tailscale.com - Description: Tailscale Mesh VPN
- Icon: Choose or upload the Tailscale icon
2. Configure OpenID Connect
- In the Authentication tab
- Select OpenID Connect as the authentication type
- Configure the settings:
- Client ID:
tailscale-xxxxxx(will be generated automatically) - Client Secret:
secret-xxxxxx(will be generated automatically) - App ID:
[appid](unique identifier of the application in VaultysHub) - Client type: Confidential
- Client ID:
3. Configure the redirect URLs
Add the following URLs in Authorized redirect URLs:
https://login.tailscale.com/a/oauth_response
https://controlplane.tailscale.com/a/oauth_response
4. Configure scopes and claims
Required scopes:
openidprofileemail
5. WebFinger Configuration (Recommended)
WebFinger allows users to log in with their email instead of the full provider URL.
Verify WebFinger configuration
Your domain (not your VaultysHub instance) must automatically expose WebFinger at:
https://<mydomain>/.well-known/webfinger
Test with:
curl "https://example.com/.well-known/webfinger"
The response should contain:
{
"links": [
{
"rel": "http://openid.net/specs/connect/1.0/issuer",
"href": "https://your-instance.link.vaultys.org/api/oidc/[appid]"
}
]
}
Configuration in Tailscale
1. Access SSO settings
- Log in to Tailscale Admin Console
- Go to Settings → Identity provider
- Click on Configure custom OIDC
2. Configure the OpenID Connect provider
Fill in the following fields:
- Issuer URL:
https://your-instance.link.vaultys.org/api/oidc/[appid] - Client ID:
[Copy from VaultysHub] - Client Secret:
[Copy from VaultysHub]
Note: The application created in VaultysHub automatically generates an
[appid]that will be used in the endpoint URLs.
3. Configure endpoints (if not auto-discovered)
If Tailscale does not automatically detect the endpoints via .well-known/openid-configuration, configure manually:
- Configuration URL:
https://your-instance.link.vaultys.org/api/oidc/[appid]/.well-known/openid-configuration - Authorization endpoint:
https://your-instance.link.vaultys.org/api/oidc/[appid]/auth - Token endpoint:
https://your-instance.link.vaultys.org/api/oidc/[appid]/token - UserInfo endpoint:
https://your-instance.link.vaultys.org/api/oidc/[appid]/userinfo - JWKS endpoint:
https://your-instance.link.vaultys.org/api/oidc/[appid]/jwks
4. Attribute mapping
User mapping configuration:
- Email claim:
email - Name claim:
nameoremail
5. Domain configuration
In Email domain requirements:
- Add your email domain:
@example.com - Enable Allow only verified domains
WebFinger configuration for simplified login
Benefits of WebFinger
With WebFinger configured, users can log in using simply:
Instead of:
https://your-instance.link.vaultys.org
Serve WebFinger on your domain
VaultysHub does not expose a WebFinger endpoint: it is the web server of your email domain (example.com) that must respond, indicating the VaultysHub issuer. A static response is sufficient:
location = /.well-known/webfinger {
default_type application/jrd+json;
return 200 '{"subject":"$arg_resource","links":[{"rel":"http://openid.net/specs/connect/1.0/issuer","href":"https://your-instance.link.vaultys.org/api/oidc/[appid]"}]}';
}
Test WebFinger with Tailscale
- On the Tailscale login page
- Enter your email:
[email protected] - Tailscale should automatically discover VaultysHub as the provider
- You will be redirected to VaultysHub for authentication
Group-based ACL configuration
1. VaultysHub Groups
VaultysHub does not pass user groups (no scope or claim groups). Groups are therefore defined directly in the Tailscale ACL policy, based on user email addresses.
2. Configure Tailscale ACLs
Example of ACL configuration in Tailscale:
{
"groups": {
"group:dev": ["tag:dev-servers"]
},
"acls": [
{
"action": "accept",
"src": ["group:admin"],
"dst": ["*:*"]
},
{
"action": "accept",
"src": ["group:dev"],
"dst": ["tag:dev-servers:*"]
}
],
"tagOwners": {
"tag:dev-servers": ["group:admin"]
}
}
Configuration testing
1. Initial login test
- Log out of Tailscale
- Go to login.tailscale.com
- Enter your corporate email
- You should be redirected to VaultysHub
- Authenticate with your VaultysHub credentials
- You should be logged into Tailscale
2. Test with the Tailscale client
- Install the Tailscale client on your device
- Click on Log in
- Use your corporate email
- The client should open a browser to VaultysHub
- After authentication, the client should connect
3. Check permissions
# Check connection status
tailscale status
# Check applied ACLs
tailscale netcheck
# Check tags and groups
tailscale debug acls
Troubleshooting
"Invalid issuer" error
Problem: Tailscale does not recognize the VaultysHub issuer
Solution:
- Verify that the issuer URL is exactly:
https://your-instance.link.vaultys.org/api/oidc/[appid] - Test OpenID discovery with your appid:
curl https://your-instance.link.vaultys.org/api/oidc/[appid]/.well-known/openid-configuration
- Ensure that the issuer in the configuration matches the one returned
WebFinger not working
Problem: Simple email login does not work
Solution:
- Test WebFinger directly:
curl "https://example.com/.well-known/webfinger?resource=acct:[email protected]"
- The response must come from your email domain, not from VaultysHub, and its
hrefmust be exactly the VaultysHub issuer - Check CORS headers if necessary
"User not authorized" error
Problem: The user cannot access Tailscale after authentication
Solution:
- Verify that the user's email matches the configured domain
- Ensure that the user is assigned to the application in VaultysHub
- Check the logs in Tailscale Admin Console
Groups not synchronized
Problem: Group-based ACLs do not work
Solution: VaultysHub does not pass groups. Define groups and their members (email addresses) directly in the Tailscale ACL policy.
SSL certificate error
Problem: SSL validation error during connection
Solution:
- Ensure you are using a valid SSL certificate (not self-signed)
- Check the complete certificate chain
- Test with:
openssl s_client -connect your-instance.example.com:443 -showcerts
Security
Best practices
- HTTPS required: Never use HTTP for OAuth endpoints
- Verified domains: Limit access to verified email domains
- Secret rotation: Regularly change the Client Secret
- Strict ACLs: Use the principle of least privilege
- Audit logs: Monitor connections in Tailscale Admin
MFA configuration
Tailscale inherits the MFA configuration from VaultysHub:
- Enable MFA in VaultysHub for users
- Users will need to authenticate with MFA when logging into Tailscale
Access revocation
To revoke a user's access:
- Disable or delete the user in VaultysHub
- Tailscale access will be automatically revoked when the token expires
- For immediate revocation, also remove the device in Tailscale Admin
Advanced configuration
Custom session expiration
In VaultysHub, configure the token lifespan:
- Access Token: 1 hour (recommended)
- Refresh Token: 30 days
- Tailscale Session: Aligns with the Refresh Token
Integration with Headscale
If you are using Headscale (open-source control server):
- Follow the Headscale guide
- The OpenID configuration is similar
- WebFinger also works with Headscale
Automation with Terraform
Example Terraform configuration for Tailscale:
resource "tailscale_acl" "main" {
acl = jsonencode({
groups = {
"group:admin" = ["[email protected]"]
}
acls = [
{
action = "accept"
src = ["group:admin"]
dst = ["*:*"]
}
]
})
}