Add Kubernetes integration tests for EasyHAProxy examples
- Introduced a suite of pytest-based integration tests for Kubernetes using `kind`. - Automated local installation of dependencies (`kind`, `kubectl`, and `helm`) when missing. - Implemented fixtures for Kubernetes resource management and TLS secrets. - Added end-to-end tests for HTTP and HTTPS ingress functionality.
This commit is contained in:
parent
5767e55dea
commit
90b01b1f13
18 changed files with 2978 additions and 24 deletions
|
|
@ -245,6 +245,219 @@ env:
|
|||
|
||||
For more information on plugin types and available plugins, see the [Using Plugins](plugins.md) guide.
|
||||
|
||||
## Loading Plugin Configuration from Kubernetes Secrets
|
||||
|
||||
EasyHAProxy supports loading sensitive plugin configuration values directly from Kubernetes Secrets using the `k8s_secret` pattern. This is a **generic, plugin-agnostic feature** that works with any plugin.
|
||||
|
||||
### Why Use Kubernetes Secrets?
|
||||
|
||||
- **Security**: Keep sensitive data (API keys, passwords, certificates) out of annotations
|
||||
- **Best practices**: Follows Kubernetes conventions for managing sensitive data
|
||||
- **Simplicity**: No need to mount volumes or ConfigMaps for secret data
|
||||
- **Encryption**: Secrets are encrypted at rest in etcd
|
||||
|
||||
### Annotation Format
|
||||
|
||||
```yaml
|
||||
# Auto-detect key (tries common variations):
|
||||
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name"
|
||||
|
||||
# Explicit key (no variations):
|
||||
easyhaproxy.plugin.{plugin_name}.k8s_secret.{config_key}: "secret_name/key_name"
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **You create** a Kubernetes Secret with your sensitive data
|
||||
2. **You reference** the secret in your ingress annotation using the `k8s_secret` pattern
|
||||
3. **EasyHAProxy reads** the secret from the same namespace as the ingress
|
||||
4. **EasyHAProxy transforms** the annotation to inject the secret value
|
||||
5. **The plugin receives** the value as if it was provided directly in the annotation
|
||||
|
||||
**Example transformation:**
|
||||
|
||||
```yaml
|
||||
# Input annotation:
|
||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "my-jwt-secret"
|
||||
|
||||
# EasyHAProxy reads the secret and transforms to:
|
||||
easyhaproxy.plugin.jwt_validator.pubkey: "<base64-encoded-content>"
|
||||
```
|
||||
|
||||
### Auto-Detect vs Explicit Key
|
||||
|
||||
#### Auto-Detect Key Format
|
||||
|
||||
When you use `"secret_name"` (without `/`), EasyHAProxy tries to find the key automatically:
|
||||
|
||||
```yaml
|
||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "my-jwt-secret"
|
||||
```
|
||||
|
||||
EasyHAProxy will try these keys in order:
|
||||
1. Exact match: `pubkey`
|
||||
2. Common variations based on the config key name
|
||||
|
||||
**Auto-detect key variations:**
|
||||
|
||||
| Config Key | Tries (in order) |
|
||||
|------------|---------------------------------------|
|
||||
| `pubkey` | `pubkey`, `public-key`, `jwt.pub`, `tls.crt` |
|
||||
| `password` | `password`, `pass`, `pwd` |
|
||||
| `api_key` | `api_key`, `apikey`, `api-key`, `key` |
|
||||
|
||||
#### Explicit Key Format
|
||||
|
||||
When you use `"secret_name/key_name"` (with `/`), EasyHAProxy only tries the exact key name:
|
||||
|
||||
```yaml
|
||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "my-jwt-secret/rsa-public-key"
|
||||
```
|
||||
|
||||
EasyHAProxy will **only** try: `rsa-public-key` (no variations)
|
||||
|
||||
**Use explicit key when:**
|
||||
- Your secret uses a non-standard key name
|
||||
- You want to be explicit and avoid ambiguity
|
||||
- Multiple keys exist in the secret
|
||||
|
||||
### Complete Example
|
||||
|
||||
```yaml
|
||||
---
|
||||
# 1. Create a secret with your JWT public key
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: jwt-pubkey-secret
|
||||
namespace: production
|
||||
type: Opaque
|
||||
stringData:
|
||||
pubkey: |
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
---
|
||||
# 2. Reference it in your ingress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: api-ingress
|
||||
namespace: production
|
||||
annotations:
|
||||
easyhaproxy.plugins: "jwt_validator"
|
||||
easyhaproxy.plugin.jwt_validator.algorithm: "RS256"
|
||||
easyhaproxy.plugin.jwt_validator.issuer: "https://auth.example.com/"
|
||||
easyhaproxy.plugin.jwt_validator.audience: "https://api.example.com"
|
||||
# Load pubkey from Kubernetes secret (auto-detect key)
|
||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "jwt-pubkey-secret"
|
||||
spec:
|
||||
ingressClassName: easyhaproxy
|
||||
rules:
|
||||
- host: api.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: api-service
|
||||
port:
|
||||
number: 8080
|
||||
```
|
||||
|
||||
### Example with Explicit Key Name
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: app-credentials
|
||||
namespace: production
|
||||
type: Opaque
|
||||
stringData:
|
||||
# Custom key name
|
||||
rsa-public-key: |
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
...
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: api-ingress
|
||||
namespace: production
|
||||
annotations:
|
||||
easyhaproxy.plugins: "jwt_validator"
|
||||
# Use explicit key name after the slash
|
||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "app-credentials/rsa-public-key"
|
||||
spec:
|
||||
ingressClassName: easyhaproxy
|
||||
# ... rest of configuration
|
||||
```
|
||||
|
||||
### Using with Any Plugin
|
||||
|
||||
The `k8s_secret` pattern works with **any plugin configuration**:
|
||||
|
||||
```yaml
|
||||
# JWT Validator - load public key
|
||||
easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "jwt-secret"
|
||||
|
||||
# Hypothetical API auth plugin - load API key
|
||||
easyhaproxy.plugin.api_auth.k8s_secret.api_key: "api-credentials/key"
|
||||
|
||||
# Hypothetical basic auth plugin - load password
|
||||
easyhaproxy.plugin.basic_auth.k8s_secret.password: "auth-secret/pwd"
|
||||
```
|
||||
|
||||
### Priority Order
|
||||
|
||||
When multiple configuration methods are used, this is the priority (highest to lowest):
|
||||
|
||||
1. **Explicit annotation** (e.g., `easyhaproxy.plugin.jwt_validator.pubkey: "value"`)
|
||||
2. **k8s_secret annotation** (e.g., `easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey: "secret"`)
|
||||
|
||||
Explicit annotations always take precedence over `k8s_secret` annotations.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Secret not found:**
|
||||
```
|
||||
WARNING: Ingress production/api-ingress - Failed to process k8s_secret annotation
|
||||
'easyhaproxy.plugin.jwt_validator.k8s_secret.pubkey' with value 'jwt-secret': ...
|
||||
```
|
||||
- Verify the secret exists: `kubectl get secret jwt-secret -n production`
|
||||
- Check the secret is in the same namespace as the ingress
|
||||
|
||||
**Key not found in secret:**
|
||||
```
|
||||
WARNING: Ingress production/api-ingress - Secret 'jwt-secret' found but no matching
|
||||
key (tried: pubkey, public-key, jwt.pub, tls.crt)
|
||||
```
|
||||
- List secret keys: `kubectl get secret jwt-secret -n production -o jsonpath='{.data}'`
|
||||
- Use explicit key format: `"jwt-secret/actual-key-name"`
|
||||
|
||||
**Check EasyHAProxy logs:**
|
||||
```bash
|
||||
kubectl logs -n easyhaproxy -l app=easyhaproxy --tail=100
|
||||
```
|
||||
|
||||
Look for:
|
||||
- `INFO: Loaded 'pubkey' from secret 'jwt-secret'` (success)
|
||||
- `WARNING: Secret 'xyz' found but no matching key` (key not found)
|
||||
|
||||
### Security Considerations
|
||||
|
||||
- Secrets are read from the **same namespace** as the ingress (no cross-namespace access)
|
||||
- EasyHAProxy needs RBAC permissions to read secrets (included in default deployment)
|
||||
- Secrets are encrypted at rest in etcd
|
||||
- Secret values are base64-encoded by Kubernetes automatically
|
||||
- Use Kubernetes RBAC to control which service accounts can read which secrets
|
||||
|
||||
## Certbot / ACME / Letsencrypt
|
||||
|
||||
It is necessary to add the annotation `easyhaproxy.certbot` to the ingress configuration:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue