Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ sbom/
!testdata/supportbundle/*.tar.gz
!test/baselines/**/baseline.tar.gz

# Ignore built binaries
troubleshoot
troubleshoot-test
# Ignore built binaries (use / prefix to avoid catching source files)
/troubleshoot
/troubleshoot-test
cmd/troubleshoot/troubleshoot
cmd/*/troubleshoot
support-bundle
/support-bundle
59 changes: 59 additions & 0 deletions examples/collect/v1beta3-bundle-secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
apiVersion: troubleshoot.sh/v1beta3
kind: SupportBundle
metadata:
name: test-v1beta3-secretref
spec:
collectors:
# Test 1: PostgreSQL with URI from secret
- postgres:
collectorName: postgres-with-secret
uri:
valueFrom:
secretKeyRef:
name: test-database-credentials
key: postgres-uri
# This will fail to connect (fake server) but that's OK -
# we're testing secret resolution, not actual DB connectivity

# Test 2: PostgreSQL with TLS certs from secret
- postgres:
collectorName: postgres-with-tls
uri:
value: "postgresql://testuser:testpass@localhost:5432/testdb"
tls:
cacert:
valueFrom:
secretKeyRef:
name: test-database-credentials
key: ca.crt
clientCert:
valueFrom:
secretKeyRef:
name: test-database-credentials
key: client.crt
clientKey:
valueFrom:
secretKeyRef:
name: test-database-credentials
key: client.key

# Test 3: MySQL with URI from secret
- mysql:
collectorName: mysql-with-secret
uri:
valueFrom:
secretKeyRef:
name: test-database-credentials
key: mysql-uri

# Test 4: Redis with URI from secret
- redis:
collectorName: redis-with-secret
uri:
valueFrom:
secretKeyRef:
name: test-database-credentials
key: redis-uri

# Test 5: Literal value (no secret) for comparison
- clusterInfo: {}
39 changes: 39 additions & 0 deletions examples/collect/v1beta3-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
# Secret containing database credentials
apiVersion: v1
kind: Secret
metadata:
name: test-database-credentials
namespace: default
type: Opaque
stringData:
# PostgreSQL connection URI
postgres-uri: "postgresql://testuser:supersecret@postgres.example.com:5432/testdb?sslmode=require"

# MySQL connection URI
mysql-uri: "mysql://testuser:supersecret@mysql.example.com:3306/testdb"

# Redis connection URI
redis-uri: "redis://:supersecret@redis.example.com:6379"

# TLS certificates (example data)
ca.crt: |
-----BEGIN CERTIFICATE-----
MIICpDCCAYwCCQDU+pQ3ZUD30jANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMjQwMTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7
VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1+fWIcPm15A8IgC0qC1J3xGhE=
-----END CERTIFICATE-----

client.crt: |
-----BEGIN CERTIFICATE-----
MIICpDCCAYwCCQDU+pQ3ZUD30jANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMjQwMTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAUMRIwEAYD
VQQDDA5jbGllbnQtY2VydA==
-----END CERTIFICATE-----

client.key: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7VJTUt9Us8cKj
MzEfYyjiWA4R4/M2bS1+fWIcPm15A8IgC0qC1J3xGhE=
-----END PRIVATE KEY-----
236 changes: 236 additions & 0 deletions examples/support-bundle/v1beta3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# v1beta3 Support Bundle Examples

This directory contains example Support Bundle specs using the v1beta3 API, which introduces `StringOrValueFrom` support for securely referencing Kubernetes Secrets and ConfigMaps in collector fields.

## Features

### StringOrValueFrom Pattern

The v1beta3 API introduces a Kubernetes-native pattern for referencing sensitive values:

```yaml
uri:
valueFrom:
secretKeyRef:
name: my-secret
key: connection-uri
```

or

```yaml
uri: "postgresql://localhost:5432/db" # Literal value
```

### Supported Collectors

Currently, v1beta3 supports `StringOrValueFrom` for:

- **Database collectors**: `postgres`, `mysql`, `redis`, `mssql`
- `uri` field - Connection strings from secrets
- `tls` fields - CA cert, client cert, and client key from secrets

## Examples

### 1. postgres-with-secret.yaml
Basic PostgreSQL collector with connection URI from a secret.

**Use case**: Securely store database credentials without hardcoding them in the spec.

```bash
kubectl apply -f postgres-with-secret.yaml
```

### 2. postgres-with-tls.yaml
PostgreSQL with TLS configuration from secrets.

**Use case**: Secure database connections with mutual TLS, storing certificates in secrets.

```bash
kubectl apply -f postgres-with-tls.yaml
```

### 3. multiple-databases.yaml
Multiple database collectors (PostgreSQL, MySQL, Redis, MSSQL) with various configurations.

**Use case**: Collect diagnostics from multiple databases in your application stack.

```bash
kubectl apply -f multiple-databases.yaml
```

### 4. cross-namespace-secrets.yaml
Accessing secrets from different namespaces.

**Use case**: Centralized credential management in a shared namespace.

```bash
kubectl apply -f cross-namespace-secrets.yaml
```

**RBAC Requirements**: The support bundle service account needs `get` permission on secrets in the referenced namespaces.

### 5. optional-secrets.yaml
Using the `optional` field for graceful degradation.

**Use case**: Collect diagnostics even when some credentials are unavailable (e.g., optional secondary databases).

```bash
kubectl apply -f optional-secrets.yaml
```

### 6. configmap-example.yaml
Using ConfigMaps for non-sensitive configuration.

**Use case**: Store non-sensitive connection strings (e.g., development databases) in ConfigMaps.

```bash
kubectl apply -f configmap-example.yaml
```

## Key Concepts

### Secret vs ConfigMap

- **Secrets**: Use for sensitive data (passwords, tokens, certificates)
- **ConfigMaps**: Use for non-sensitive configuration (development endpoints, feature flags)

### Optional Field

```yaml
uri:
valueFrom:
secretKeyRef:
name: my-secret
key: uri
optional: true # Returns empty string if secret/key doesn't exist
```

- `optional: false` (default): Collection fails if secret is missing
- `optional: true`: Returns empty string if secret/key is missing

### Cross-Namespace Access

```yaml
uri:
valueFrom:
secretKeyRef:
name: shared-secret
key: uri
namespace: other-namespace # Access secrets in different namespaces
```

If `namespace` is not specified, uses the support bundle's namespace.

### Backward Compatibility

v1beta3 maintains backward compatibility with v1beta2 TLS configuration:

```yaml
tls:
secret: # v1beta2 style
name: tls-secret
namespace: default
```

## RBAC Configuration

Support bundles need appropriate RBAC permissions to read secrets:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: troubleshoot-secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["postgres-connection", "redis-creds"] # Restrict to specific secrets
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: troubleshoot-secret-reader-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: troubleshoot-secret-reader
subjects:
- kind: ServiceAccount
name: troubleshoot
namespace: default
```

## Migration from v1beta2

### Before (v1beta2):
```yaml
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
spec:
collectors:
- postgres:
uri: "postgresql://user:password@host:5432/db" # Hardcoded
```

### After (v1beta3):
```yaml
apiVersion: troubleshoot.sh/v1beta3
kind: SupportBundle
spec:
collectors:
- postgres:
uri:
valueFrom:
secretKeyRef:
name: postgres-connection
key: connection-uri
```

## Limitations

1. **No value composition**: Cannot combine multiple secrets into a single value
```yaml
# NOT SUPPORTED
uri: "postgresql://$(USERNAME):$(PASSWORD)@host:5432/db"
```
Store the complete connection string in a single secret key.

2. **Collector scope**: Only database collectors support `StringOrValueFrom` initially
- Future versions will extend to HTTP, Data, and other collectors

3. **No templating**: The entire field value comes from one source

## Security Best Practices

1. **Use resourceNames in RBAC**: Restrict access to specific secrets
2. **Separate secrets**: Don't reuse secrets across applications
3. **Rotate credentials**: Update secrets regularly
4. **Audit access**: Monitor secret access logs
5. **Redact output**: Ensure connection strings are redacted in bundle output

## Troubleshooting

### Error: "failed to get secret default/my-secret"

- **Cause**: Secret doesn't exist or RBAC denied access
- **Solution**: Verify secret exists: `kubectl get secret my-secret`
- **Solution**: Check RBAC: `kubectl auth can-i get secret/my-secret`

### Error: "key 'uri' not found in secret"

- **Cause**: Secret exists but doesn't contain the specified key
- **Solution**: Check secret keys: `kubectl get secret my-secret -o jsonpath='{.data}'`

### Error: "cannot specify both 'value' and 'valueFrom'"

- **Cause**: Both literal value and secret reference provided
- **Solution**: Use only one: either `value: "string"` or `valueFrom: {...}`

## Additional Resources

- [Troubleshoot Documentation](https://troubleshoot.sh)
- [v1beta3 API Reference](https://troubleshoot.sh/docs/v1beta3/)
- [Kubernetes Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)
- [RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
Loading
Loading