Execution is done via terraform:
- Terraform Base - Install infrastructure components
- Terraform Apps - Install Apps
This differentiation is necessary because there're several dependencies between infrastructure and apps. Additionaly almost all apps require a Vault ID which can only be fetched after Vault is configured.
Currently there are 2 environments:
- dev => Which is a kind cluster, using a local terraform state
- prd => Which is a full cluster with several machines using a remote s3 terraform state
You need to configure AWS_S3_ENDPOINT, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY as env vars
cd terraform-apps OR terraform-base
../tfinit.sh prod
cd terraform-apps OR terraform-base
../tfinit.sh local
Currently due to my integration with 1password I created a file with 1p references and I ran terraform as follows:
Example of .env.prd:
TF_VAR_my_ip=op://MY_VAULT/SECRET_NAME/FIELD
1password fetch this passwords based on references above and inject while running terraform with:
op run --env-file=envs/.env.prd --no-masking -- terraform COMMAND
Where COMMAND can be plan, apply....
Teoretically since now my secrets are not text-clear I could commit to git but better not provide any information on my Vault Name to the outside
Besides that for apps, I provide a terraform.tfars with some maps. Sadly it's not easy to provider map of objects using TF_VAR sintax
Ideas and project track can be found here
Pod Security Admissions
Applications in this repo assume Pod Security Adminission as baseline enabled by default therefore settings are adjusted to it
If you want to run this localy on laptop do:
kind create cluster
Contains the Base Infrastructure
Execution Order:
- Storage
- Autoscaling (optional for hpa otw you need redeploy)
- Metallb
- Nginx
- 1password_connect
- CSI Secret Store
- Hashicorp Vault
- Cert-Manager
[ APP: Require storage]
|
v
[ Storage Class with CSI NFS Driver ]
This creates storage for PVs
When I created the NFS I set a user and group to it. I restricted access to K8s machines.
When creating a NFS permissions are as follows:
drwx---- root wheel
In the NFS I had also to configure maproot_user to root and maproot_group to wheel, this is called no_root_squash permissions are absolutely necessary for CSI NFS be able to do fsGroupChangePolicy and allow CSI to delete from NFS
I wanted to strict permissions per application on NFS as much as possible. So I tried to play only by assigning app_user to nfs_group and then don't use fsGroup or fsGroupChangePolicy. Vault needs to stat the root directory so I added 711 but that didn't work and if I did more permissions would breach security. So there's no running from fsGroup and fsGroupChangePolicy.
If you set an fsGroup, fsGroupChangePolicy will run and set that as the group.
In the pod do:
securityContext:
runAsUser: app_user
runAsGroup: app_group
fsGroup: app_group
fsGroupChangePolicy: "OnRootMismatch"
Minio was deprecated and is now in maintenance mode. Seaweedfs is the replacement
Metric server is installed to enable HPA
[ MetalLB: Assigns IP to Nginx ]
|
v
[ Nginx: Reverse Proxy / Load Balancer ]
MetalLB is an open-source load-balancer implementation for Kubernetes clusters running on bare-metal environments. Unlike cloud platforms that provide native load balancers, MetalLB enables Kubernetes services to expose public-facing IP addresses by integrating with existing network infrastructure. It supports key protocols like Layer 2 and BGP (Border Gateway Protocol) to manage traffic efficiently, making it ideal for environments without cloud-native load-balancing solutions.
MetalLB requires a pool of IP addresses in order to be able to take ownership of the ingress-nginx Service. This pool can be defined through IPAddressPool objects in the same namespace as the MetalLB controller. This pool of IPs must be dedicated to MetalLB's use, you can't reuse the Kubernetes node IPs or IPs handed out by a DHCP server.
IP pool and advertisement can only be created on metallb namespace. It's always necessary to create both and an advertisement points to a pool
[ Application needs certificate ]
|
|
v
[ Creates an ingress ]
|
|
v
[ Cert Manager detects ingress and issue a certificate]
|
|
v
[ Cert Manager an ISSUER to sign the certificate and manages rotation]
We have currently 3 issuers:
- Manual Uploaded Issuer which is a private CA that was uploaded to cert manager. This allows Vault to be installed using a Vault TLS of it's own without relying on Kubernetes CA.
- Hashicorp Vault issuer: We uploaded another private CA to Hashicorp Vault PKI and sensitive apps make use of this certificate chain
- Let's Encrypt issuer: For other apps which require a Public Authority like TVs and embedded
[ Hashicorp Vault: reads from 1password ]
|
v
[ 1password Connect: 1password component]
| |
v v
[CSI Secret Store ] [Vault Agent]
Vault Agent: Injects secrets into pods using environment variables or file. Sadly it doesn't create Kubernetes Secretes.
CSI Secret Store & Vault Provider: Creates Kubernetes Secret from Secret
Since Vault installation is by far the most complex component a separate README in it's modules
We use Velero for Backups. It authenticates with an objet store to create the necessary assets using environment variables