diff --git a/modules/kubernetes/logo.png b/modules/kubernetes/logo.png new file mode 100644 index 0000000..8c3e0d1 Binary files /dev/null and b/modules/kubernetes/logo.png differ diff --git a/modules/kubernetes/service-account/buildingblock/APP_TEAM_README.md b/modules/kubernetes/service-account/buildingblock/APP_TEAM_README.md new file mode 100644 index 0000000..7628f29 --- /dev/null +++ b/modules/kubernetes/service-account/buildingblock/APP_TEAM_README.md @@ -0,0 +1,145 @@ +# Kubernetes Service Account + +This building block creates a Kubernetes service account in your namespace with role-based access to cluster resources. Service accounts are used for automated authentication and authorization in CI/CD pipelines, applications, and automation scripts running within or outside the cluster. + +## 🚀 Usage Examples + +- A development team creates a service account to **automate deployments** from their CI/CD pipelines to Kubernetes resources. +- A DevOps engineer sets up service accounts with **view access** for monitoring tools that need to observe cluster state. +- A team configures a service account with **edit permissions** for their application to manage resources within their namespace. +- An operations team uses the generated **kubeconfig** to configure external tools like ArgoCD or Flux. + +## 🔄 Shared Responsibility + +| Responsibility | Platform Team | Application Team | +|----------------|---------------|------------------| +| Create service account | ✅ | ❌ | +| Assign ClusterRole to service account | ✅ | ❌ | +| Provide kubeconfig credentials | ✅ | ❌ | +| Store kubeconfig securely | ❌ | ✅ | +| Use service account in pipelines/applications | ❌ | ✅ | +| Monitor service account usage | ✅ | ✅ | +| Use least privilege roles | ⚠️ | ✅ | +| Request removal of unused service accounts | ❌ | ✅ | + +## 💡 Best Practices + +### Service Account Naming + +**Why**: Clear names help identify purpose and ownership. + +**Recommended Patterns**: +- Include application/service name: `myapp-production-sa` +- Include purpose: `myapp-cicd-sa`, `myapp-monitoring-sa` +- Include environment: `myapp-dev-sa`, `myapp-prod-sa` + +**Examples**: +- ✅ `ecommerce-prod-deployment-sa` +- ✅ `analytics-monitoring-sa` +- ✅ `backup-automation-sa` +- ❌ `sa1` +- ❌ `test-service-account` + +### Role Selection + +**Why**: Follow least privilege principle to minimize security risks. + +| Role | Use Case | +|------|----------| +| `view` | Read-only access for monitoring, dashboards, debugging | +| `edit` | Deploy and manage applications within namespace | +| `admin` | Full namespace administration including RBAC | + +**Recommendations**: +- Start with `view` and escalate only if needed +- Use `edit` for CI/CD pipelines that deploy applications +- Reserve `admin` for tools that need to manage RBAC or other privileged resources + +### Kubeconfig Management + +**Why**: The kubeconfig contains sensitive credentials that grant access to your namespace. + +**Best Practices**: +- Store kubeconfig in a secure secrets manager (HashiCorp Vault, AWS Secrets Manager, etc.) +- Never commit kubeconfig to version control +- Rotate service account tokens periodically +- Use short-lived tokens where possible + +**Example Usage**: +```bash +# Save kubeconfig to a file +echo "$KUBECONFIG_CONTENT" > kubeconfig + +# Use with kubectl +kubectl --kubeconfig kubeconfig get pods + +# Or set KUBECONFIG environment variable +export KUBECONFIG=./kubeconfig +kubectl get pods +``` + +### CI/CD Integration + +**GitHub Actions Example**: +```yaml +steps: + - name: Configure kubectl + run: | + echo "${{ secrets.KUBECONFIG }}" > kubeconfig + kubectl --kubeconfig kubeconfig apply -f k8s/ +``` + +**GitLab CI Example**: +```yaml +deploy: + script: + - echo "$KUBECONFIG" > kubeconfig + - kubectl --kubeconfig kubeconfig apply -f k8s/ +``` + +## ⚠️ Security Considerations + +### Token Security + +- The service account token provides **long-lived** access to the cluster +- Anyone with the kubeconfig can access resources according to the assigned role +- Treat the kubeconfig as a secret credential + +### Namespace Isolation + +- The role binding is **namespace-scoped** +- The service account cannot access resources in other namespaces +- Cross-namespace access requires additional configuration + +### Audit and Monitoring + +- All actions performed by the service account are **logged in Kubernetes audit logs** +- Work with your platform team to set up **alerting** for suspicious activity +- Regularly review service account usage and remove unused accounts + +## 📋 Troubleshooting + +### Common Issues + +**"Forbidden" errors when using kubectl**: +- Verify the assigned ClusterRole has the required permissions +- Check if you're operating in the correct namespace +- Ensure the kubeconfig context is set correctly + +**Token not working**: +- Verify the secret was created correctly +- Check if the service account exists +- Ensure the cluster CA certificate is correct + +**Unable to connect to cluster**: +- Verify the cluster endpoint is reachable from your network +- Check firewall rules and network policies +- Ensure the cluster CA certificate matches the cluster + +### Getting Help + +Contact your platform team if you: +- Need elevated permissions beyond `edit` +- Require access to cluster-wide resources +- Experience persistent authentication issues +- Need to rotate or regenerate credentials diff --git a/modules/kubernetes/service-account/buildingblock/README.md b/modules/kubernetes/service-account/buildingblock/README.md new file mode 100644 index 0000000..e1b29fd --- /dev/null +++ b/modules/kubernetes/service-account/buildingblock/README.md @@ -0,0 +1,69 @@ +--- +name: Kubernetes Service Account +supportedPlatforms: + - kubernetes +description: Creates a Kubernetes service account with ClusterRole binding and generates a kubeconfig for authentication +--- + +# Kubernetes Service Account Building Block + +Creates and manages a Kubernetes service account with role binding to a specified ClusterRole, and generates a kubeconfig file for authentication. + +This documentation is intended as a reference for cloud foundation or platform engineers using this module. + +## Prerequisites + +- Access to a Kubernetes cluster +- A service account with permissions to create service accounts, secrets, and role bindings +- Cluster CA certificate (base64 encoded) +- Token for the service account executing this module + +## Features + +- Creates a Kubernetes service account in a specified namespace +- Creates a secret with service account token +- Binds the service account to a specified ClusterRole (admin, edit, view, or custom) +- Generates a ready-to-use kubeconfig file as output + +> ⚠️ **Security Notice**: The `kubeconfig` output contains a service account token that grants access to the Kubernetes cluster. When displayed as plain text in meshStack, this sensitive credential will be visible to users who can view the building block outputs. Ensure that only authorized users have access to view these outputs, and advise users to store the kubeconfig securely after retrieval. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [kubernetes](#requirement\_kubernetes) | ~> 2.38 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [kubernetes_role_binding.this](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_binding) | resource | +| [kubernetes_secret.this](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_service_account.this](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cluster\_ca\_certificate](#input\_cluster\_ca\_certificate) | Cluster CA certificate, base64 encoded | `string` | n/a | yes | +| [cluster\_endpoint](#input\_cluster\_endpoint) | IP address of the cluster control plane | `string` | n/a | yes | +| [cluster\_name](#input\_cluster\_name) | Name of the k8s cluster hosting this service account | `string` | n/a | yes | +| [cluster\_role](#input\_cluster\_role) | ClusterRole to bind the service account with. e.g. admin, edit, view (or any custom cluster role) | `string` | n/a | yes | +| [context](#input\_context) | Defines which cluster to interact with. Can be any name | `string` | n/a | yes | +| [name](#input\_name) | Service account name | `string` | n/a | yes | +| [namespace](#input\_namespace) | Namespace where the service account will be created. Recommended: Use platform tenant ID as input in meshStack | `string` | n/a | yes | +| [token](#input\_token) | Token for the service account executing this module (not this service account) | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [instructions](#output\_instructions) | Instructions for using the kubeconfig | +| [kubeconfig](#output\_kubeconfig) | Kubeconfig file content for authenticating with the Kubernetes cluster | + diff --git a/modules/kubernetes/service-account/buildingblock/logo.png b/modules/kubernetes/service-account/buildingblock/logo.png new file mode 100644 index 0000000..df4ce5c Binary files /dev/null and b/modules/kubernetes/service-account/buildingblock/logo.png differ diff --git a/modules/kubernetes/service-account/buildingblock/main.tf b/modules/kubernetes/service-account/buildingblock/main.tf new file mode 100644 index 0000000..4524bf4 --- /dev/null +++ b/modules/kubernetes/service-account/buildingblock/main.tf @@ -0,0 +1,35 @@ +resource "kubernetes_service_account" "this" { + metadata { + name = var.name + namespace = var.namespace + } +} + +resource "kubernetes_secret" "this" { + metadata { + name = kubernetes_service_account.this.metadata[0].name + namespace = kubernetes_service_account.this.metadata[0].namespace + annotations = { + "kubernetes.io/service-account.name" = kubernetes_service_account.this.metadata[0].name + } + } + + type = "kubernetes.io/service-account-token" +} + +resource "kubernetes_role_binding" "this" { + metadata { + name = "${var.name}-${var.cluster_role}" + namespace = var.namespace + } + subject { + kind = "ServiceAccount" + name = var.name + namespace = var.namespace + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = var.cluster_role + } +} diff --git a/modules/kubernetes/service-account/buildingblock/outputs.tf b/modules/kubernetes/service-account/buildingblock/outputs.tf new file mode 100644 index 0000000..7f58de6 --- /dev/null +++ b/modules/kubernetes/service-account/buildingblock/outputs.tf @@ -0,0 +1,44 @@ +locals { + kubeconfig = { + apiVersion = "v1" + kind = "Config" + + users = [{ + name = kubernetes_service_account.this.metadata[0].name + user = { + token = kubernetes_secret.this.data["token"] + } + }] + + clusters = [{ + cluster = { + certificate-authority-data = var.cluster_ca_certificate + server = "https://${var.cluster_endpoint}" + } + name = var.cluster_name + } + ] + + contexts = [{ + context = { + cluster = var.cluster_name + namespace = var.namespace + user = kubernetes_service_account.this.metadata[0].name + } + name = var.context + }] + + current-context = var.context + } +} + +output "instructions" { + description = "Instructions for using the kubeconfig" + value = "Copy kubeconfig value into a file, which can be directly used. e.g. `kubectl --kubeconfig kubeconfig get pods`" +} + +output "kubeconfig" { + description = "Kubeconfig file content for authenticating with the Kubernetes cluster" + sensitive = true + value = yamlencode(local.kubeconfig) +} diff --git a/modules/kubernetes/service-account/buildingblock/provider.tf b/modules/kubernetes/service-account/buildingblock/provider.tf new file mode 100644 index 0000000..c6f5f4a --- /dev/null +++ b/modules/kubernetes/service-account/buildingblock/provider.tf @@ -0,0 +1,5 @@ +provider "kubernetes" { + host = "https://${var.cluster_endpoint}" + cluster_ca_certificate = base64decode(var.cluster_ca_certificate) + token = var.token +} diff --git a/modules/kubernetes/service-account/buildingblock/variables.tf b/modules/kubernetes/service-account/buildingblock/variables.tf new file mode 100644 index 0000000..3ef937d --- /dev/null +++ b/modules/kubernetes/service-account/buildingblock/variables.tf @@ -0,0 +1,40 @@ +variable "name" { + type = string + description = "Service account name" +} + +variable "namespace" { + type = string + description = "Namespace where the service account will be created. Recommended: Use platform tenant ID as input in meshStack" +} + +variable "cluster_name" { + type = string + description = "Name of the k8s cluster hosting this service account" +} + +variable "cluster_endpoint" { + type = string + description = "IP address of the cluster control plane" +} + +variable "cluster_ca_certificate" { + type = string + description = "Cluster CA certificate, base64 encoded" +} + +variable "context" { + type = string + description = "Defines which cluster to interact with. Can be any name" +} + +variable "cluster_role" { + type = string + description = "ClusterRole to bind the service account with. e.g. admin, edit, view (or any custom cluster role)" +} + +variable "token" { + type = string + sensitive = true + description = "Token for the service account executing this module (not this service account)" +} diff --git a/modules/kubernetes/service-account/buildingblock/versions.tf b/modules/kubernetes/service-account/buildingblock/versions.tf new file mode 100644 index 0000000..266b2a9 --- /dev/null +++ b/modules/kubernetes/service-account/buildingblock/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.38" + } + } +} diff --git a/website/src/app/shared/platform/platform.service.ts b/website/src/app/shared/platform/platform.service.ts index d080941..ac81735 100644 --- a/website/src/app/shared/platform/platform.service.ts +++ b/website/src/app/shared/platform/platform.service.ts @@ -60,6 +60,8 @@ export class PlatformService { return { name: 'GitHub', logo: logoUrl }; case 'aks': return { name: 'Azure Kubernetes Service', logo: logoUrl }; + case 'kubernetes': + return { name: 'Kubernetes', logo: logoUrl }; case 'ionos': return { name: 'IONOS', logo: logoUrl }; case 'stackit':