Authentication
le-cert-server supports two authentication methods:
- Pre-Shared Key (PSK) Authentication - Simple and secure for most use cases
- JWT/OIDC Authentication - Advanced scenarios with OAuth2 providers or platform identities
Pre-Shared Key (PSK) Authentication#
PSK authentication is a simpler and secure option for most deployments. Configure a single API key that clients use to authenticate:
auth:
psk:
key: "your-secure-random-key-here"Generate a secure key:
# Generate a random 32-byte key (base64 encoded)
openssl rand -base64 32Clients authenticate by passing the key in the Authorization header:
curl -X POST https://le-cert-server:8443/api/certificate \
-H "Authorization: APIKey your-secure-random-key-here" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'Security notice: When authenticating via PSK, all callers have access to all certificates. For fine-grained access control to specific certificates, use the JWT/OIDC authentication method.
JWT/OIDC Authentication (Advanced)#
For advanced scenarios, le-cert-server supports JWT token validation. This is useful when:
- Using OAuth2/OIDC providers (Auth0, Okta, Keycloak, etc.)
- Leveraging platform-managed identities (Azure Managed Identity, AWS IAM roles, Kubernetes service accounts, etc)
- Implementing fine-grained access control with scopes
Basic JWT Configuration:
auth:
issuerUrl: "https://your-auth-provider.com"
audience: "your-api-audience"
# Optional
requiredScopes:
- "read:certificates"
- "write:certificates"Example: Auth0 with Client Credentials Flow#
Ideal for machine-to-machine authentication:
1. Configure Auth0:
- Create an API in Auth0 dashboard with identifier
https://le-cert-server-api - Create a Machine-to-Machine application
- Authorize the application to access your API
- Define custom scopes (optional)
2. Configure le-cert-server:
auth:
issuerUrl: "https://your-tenant.auth0.com"
audience: "https://le-cert-server-api"
requiredScopes:
- "request:certificates"3. Client obtains token and requests certificate:
# Get access token using client credentials
ACCESS_TOKEN=$(curl --request POST \
--url 'https://your-tenant.auth0.com/oauth/token' \
--header 'content-type: application/json' \
--data '{
"client_id":"YOUR_CLIENT_ID",
"client_secret":"YOUR_CLIENT_SECRET",
"audience":"https://le-cert-server-api",
"grant_type":"client_credentials"
}' | jq -r '.access_token')
# Request certificate
curl -X POST https://le-cert-server:8443/api/certificate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'Example: Azure Managed Identity#
Use Azure Managed Identities to authenticate from an Azure VM, App Service, or other Azure services, without managing credentials:
1. Configure Microsoft Entra ID:
- Create an App Registration in Entra ID (Azure AD)
- Note the Application (client) ID: this is your “audience” value
- Assign Managed Identity to your Azure resources (VM, App Service, etc)
- Grant the Managed Identity access to the App Registration
2. Configure le-cert-server:
auth:
issuerUrl: "https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0"
audience: "YOUR_APP_CLIENT_ID"3. Client uses Managed Identity to get token:
# From Azure VM or App Service with Managed Identity
ACCESS_TOKEN=$(curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=YOUR_APP_CLIENT_ID' \
-H 'Metadata: true' | jq -r '.access_token')
# Request certificate
curl -X POST https://le-cert-server:8443/api/certificate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'Azure Managed Identity Benefits:
- No credentials to manage or rotate
- Automatic token refresh by Azure platform
- Integrates with Azure RBAC and identity governance
Example: AWS IAM Roles with OIDC#
Use AWS IAM roles and OIDC federation for credential-free authentication:
1. Set up OIDC Provider in AWS IAM:
- Configure an OIDC identity provider pointing to your auth server
- Create an IAM role with trust policy for the OIDC provider
- EC2 instances or ECS tasks assume this role
2. Configure le-cert-server:
auth:
issuerUrl: "https://your-oidc-provider.com"
audience: "sts.amazonaws.com"3. Client uses AWS STS to exchange IAM credentials for OIDC token:
# Get OIDC token from AWS STS
ACCESS_TOKEN=$(aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::ACCOUNT:role/le-cert-server-role \
--role-session-name cert-request \
--web-identity-token $(cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token) \
--query 'Credentials.SessionToken' \
--output text)
# Request certificate
curl -X POST https://le--server:8443/api/certificate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'Example: GCP Service Account with Workload Identity#
Use GCP Workload Identity for GKE workloads or service accounts:
1. Set up Workload Identity:
- Enable Workload Identity on your GKE cluster
- Create a service account and bind it to Kubernetes service account
- Grant the service account appropriate IAM roles
2. Configure le-cert-server:
auth:
issuerUrl: "https://accounts.google.com"
audience: "YOUR_CLIENT_ID.apps.googleusercontent.com"3. Client uses GCP metadata service to get identity token:
# From GKE pod with Workload Identity
ACCESS_TOKEN=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=YOUR_CLIENT_ID.apps.googleusercontent.com" \
-H "Metadata-Flavor: Google")
# Request certificate
curl -X POST https://le-cert-server:8443/api/certificate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'Example: tsiam (Tailscale workload identity)#
tsiam issues short-lived JWTs to machines based on their Tailscale identity (a Tailscale-native equivalent of AWS IAM Roles or Azure Managed Identity). Your nodes authenticate to le-cert-server and prove who they are simply by being on your tailnet.
Because tsiam tokens are validated over standard JWT/OIDC, le-cert-server can stay on a regular TCP listener and does not itself need to run on the tailnet: it only needs network access to tsiam’s OIDC discovery and JWKS endpoints (reachable over the tailnet, or publicly via Tailscale Funnel).
1. Allow the le-cert-server audience in tsiam (tsiam’s config.yaml):
tokens:
allowedAudiences:
- "https://le-cert-server"2. Grant nodes access in your Tailscale ACL policy. This controls which machines may request a token for the le-cert-server audience:
{
"grants": [
{
"src": ["tag:traefik"],
"dst": ["tag:tsiam"],
"app": {
"italypaleale.me/tsiam": [
{ "allowedAudiences": ["https://le-cert-server"] }
]
}
}
]
}3. Configure le-cert-server to trust tokens issued by tsiam:
auth:
method: jwt
jwt:
# tsiam's OIDC issuer URL: its MagicDNS name on your tailnet, or a Tailscale Funnel URL
# Must match the "iss" claim in the tokens tsiam issues
# le-cert-server discovers the JWKS endpoint via {issuerUrl}/.well-known/openid-configuration
issuerUrl: "https://tsiam.your-tailnet.ts.net"
# Must match the audience the client requests from tsiam (and an entry in tsiam's allowedAudiences)
audience: "https://le-cert-server"4. Client fetches a token from tsiam, then requests a certificate:
# From any node on the tailnet, request a token scoped to the le-cert-server audience
ACCESS_TOKEN=$(curl -s -X POST "https://tsiam/token?resource=https://le-cert-server" \
-H "X-Tsiam: 1" | jq -r '.access_token')
# Request certificate
curl -X POST https://le-cert-server:8443/api/certificate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'