diff --git a/applications/lionsdev-client/environments/production/kustomization.yaml b/applications/lionsdev-client/environments/production/kustomization.yaml new file mode 100644 index 0000000..855cd48 --- /dev/null +++ b/applications/lionsdev-client/environments/production/kustomization.yaml @@ -0,0 +1,70 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + name: lionsdev-client-production + annotations: + config.kubernetes.io/local-config: "true" + +namespace: lions-apps + +resources: +- ../../kubernetes/namespace.yaml +- ../../kubernetes/configmap.yaml +- ../../kubernetes/secret.yaml +- ../../kubernetes/rbac.yaml +- ../../kubernetes/deployment.yaml +- ../../kubernetes/service.yaml +- ../../kubernetes/ingress.yaml +- ../../kubernetes/hpa.yaml + +images: +- name: registry.lions.dev/lionsdev/lionsdev-client + newTag: latest + +replicas: +- name: lionsdev-client + count: 3 + +patchesStrategicMerge: +- patches/configmap-production.yaml +- patches/ingress-production.yaml +- patches/deployment-production.yaml + +patchesJson6902: +- target: + group: apps + version: v1 + kind: Deployment + name: lionsdev-client + path: patches/deployment-resources.yaml + +configMapGenerator: +- name: lionsdev-client-production-config + literals: + - ENVIRONMENT=production + - DOMAIN=lions.dev + - LOG_LEVEL=INFO + - QUARKUS_PROFILE=prod + - CLUSTER_NAME=k2 + - REPLICA_COUNT=3 + +secretGenerator: +- name: lionsdev-client-production-secrets + literals: + - DATABASE_URL=jdbc:postgresql://postgresql-service.postgresql.svc.cluster.local:5432/lionsdb + - VAULT_ADDR=https://vault.lions.dev + - PROMETHEUS_URL=https://prometheus.lions.dev + - GRAFANA_URL=https://grafana.lions.dev + +commonLabels: + environment: production + cluster: k2 + tier: frontend + app.kubernetes.io/instance: lionsdev-client-production + +commonAnnotations: + deployment.kubernetes.io/environment: production + deployment.kubernetes.io/cluster: k2 + deployment.kubernetes.io/managed-by: lionsctl + contact: gbanedahoud@gmail.com diff --git a/applications/lionsdev-client/environments/production/patches/configmap-production.yaml b/applications/lionsdev-client/environments/production/patches/configmap-production.yaml new file mode 100644 index 0000000..d9aff67 --- /dev/null +++ b/applications/lionsdev-client/environments/production/patches/configmap-production.yaml @@ -0,0 +1,102 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: lionsdev-client-config + namespace: lions-apps +data: + # Production-specific application configuration + application.properties: | + # Quarkus Configuration + quarkus.application.name=lionsdev-client + quarkus.application.version=1.0.0 + + # HTTP Configuration - Production + quarkus.http.port=8080 + quarkus.http.host=0.0.0.0 + quarkus.http.cors=true + quarkus.http.cors.origins=https://lions.dev,https://www.lions.dev + + # Database Configuration - Production + quarkus.datasource.db-kind=postgresql + quarkus.datasource.jdbc.url=jdbc:postgresql://postgresql-service.postgresql.svc.cluster.local:5432/lionsdb + quarkus.datasource.jdbc.max-size=50 + quarkus.datasource.jdbc.min-size=10 + quarkus.datasource.jdbc.acquisition-timeout=30 + quarkus.datasource.jdbc.leak-detection-interval=10M + + # Hibernate Configuration - Production + quarkus.hibernate-orm.database.generation=validate + quarkus.hibernate-orm.log.sql=false + quarkus.hibernate-orm.sql-load-script=no-file + quarkus.hibernate-orm.statistics=false + + # Logging Configuration - Production + quarkus.log.level=INFO + quarkus.log.category."dev.lions".level=INFO + quarkus.log.category."org.hibernate".level=WARN + quarkus.log.category."io.quarkus".level=INFO + quarkus.log.console.enable=true + quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n + quarkus.log.console.json=false + + # Health Check Configuration + quarkus.health.extensions.enabled=true + quarkus.smallrye-health.root-path=/health + quarkus.smallrye-health.liveness-path=/health/live + quarkus.smallrye-health.readiness-path=/health/ready + quarkus.smallrye-health.startup-path=/health/started + + # Metrics Configuration - Production + quarkus.micrometer.enabled=true + quarkus.micrometer.export.prometheus.enabled=true + quarkus.micrometer.export.prometheus.path=/metrics + quarkus.micrometer.binder.jvm=true + quarkus.micrometer.binder.system=true + quarkus.micrometer.binder.http-server.enabled=true + + # OpenAPI Configuration - Production (disabled for security) + quarkus.swagger-ui.enable=false + quarkus.openapi.path=/api/openapi + mp.openapi.extensions.smallrye.info.title=Lions Dev Client API + mp.openapi.extensions.smallrye.info.version=1.0.0 + mp.openapi.extensions.smallrye.info.description=Lions Dev professional website and client portal + mp.openapi.extensions.smallrye.info.contact.email=contact@lions.dev + mp.openapi.extensions.smallrye.info.contact.name=Lions Dev Team + mp.openapi.extensions.smallrye.info.contact.url=https://lions.dev + + # Security Configuration - Production + quarkus.http.auth.basic=false + quarkus.security.jpa.enabled=false + quarkus.http.ssl.certificate.reload-period=24H + + # Static Resources Configuration - Production + quarkus.http.static-resources."/"=META-INF/resources + quarkus.http.static-resources.cache-control=max-age=31536000, public, immutable + quarkus.http.enable-compression=true + + # Performance Configuration + quarkus.thread-pool.core-threads=8 + quarkus.thread-pool.max-threads=50 + quarkus.thread-pool.queue-size=1000 + + # Production environment configuration + environment: "production" + domain: "lions.dev" + log-level: "INFO" + cluster: "k2" + + # Lions Dev Production Configuration + lions.dev.company.name=Lions Dev + lions.dev.company.email=contact@lions.dev + lions.dev.company.phone=+225 01 01 75 95 25 + lions.dev.company.address=Abidjan, CΓ΄te d'Ivoire + lions.dev.company.website=https://lions.dev + lions.dev.company.description=Enterprise digital transformation partner delivering mission-critical solutions across Africa + + # Production feature flags + lions.dev.features.analytics.enabled=true + lions.dev.features.monitoring.enabled=true + lions.dev.features.contact-form.enabled=true + lions.dev.features.newsletter.enabled=true + lions.dev.features.debug.enabled=false + lions.dev.features.swagger.enabled=false diff --git a/applications/lionsdev-client/environments/production/patches/deployment-production.yaml b/applications/lionsdev-client/environments/production/patches/deployment-production.yaml new file mode 100644 index 0000000..b209069 --- /dev/null +++ b/applications/lionsdev-client/environments/production/patches/deployment-production.yaml @@ -0,0 +1,70 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lionsdev-client + namespace: lions-apps + annotations: + deployment.kubernetes.io/environment: "production" + deployment.kubernetes.io/cluster: "k2" + deployment.kubernetes.io/domain: "lions.dev" +spec: + replicas: 3 + template: + metadata: + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/metrics" + deployment.kubernetes.io/environment: "production" + spec: + containers: + - name: lionsdev-client + image: registry.lions.dev/lionsdev/lionsdev-client:latest + env: + - name: QUARKUS_PROFILE + value: "prod" + - name: ENVIRONMENT + value: "production" + - name: CLUSTER_NAME + value: "k2" + - name: DOMAIN + value: "lions.dev" + - name: JAVA_OPTS + value: "-Xms512m -Xmx1024m -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+OptimizeStringConcat" + resources: + requests: + memory: "512Mi" + cpu: "300m" + limits: + memory: "1Gi" + cpu: "1000m" + livenessProbe: + httpGet: + path: /health/live + port: http + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + readinessProbe: + httpGet: + path: /health/ready + port: http + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + startupProbe: + httpGet: + path: /health/started + port: http + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 60 + successThreshold: 1 diff --git a/applications/lionsdev-client/environments/production/patches/ingress-production.yaml b/applications/lionsdev-client/environments/production/patches/ingress-production.yaml new file mode 100644 index 0000000..4ff77fc --- /dev/null +++ b/applications/lionsdev-client/environments/production/patches/ingress-production.yaml @@ -0,0 +1,100 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: lionsdev-client-ingress + namespace: lions-apps + annotations: + # Production-specific Nginx configurations + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/use-regex: "true" + + # SSL/TLS Configuration for Production + cert-manager.io/cluster-issuer: "letsencrypt-prod" + cert-manager.io/acme-challenge-type: "http01" + + # Production performance optimizations + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "30" + nginx.ingress.kubernetes.io/proxy-send-timeout: "30" + nginx.ingress.kubernetes.io/proxy-read-timeout: "30" + nginx.ingress.kubernetes.io/proxy-buffering: "on" + nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" + nginx.ingress.kubernetes.io/proxy-buffers-number: "8" + + # Production caching strategy + nginx.ingress.kubernetes.io/server-snippet: | + # Static assets - long-term caching + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|webp|avif)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + add_header X-Content-Type-Options "nosniff"; + add_header Vary "Accept-Encoding"; + gzip_static on; + } + + # HTML files - short-term caching + location ~* \.(html|htm)$ { + expires 1h; + add_header Cache-Control "public, must-revalidate"; + add_header X-Content-Type-Options "nosniff"; + add_header X-Frame-Options "DENY"; + add_header X-XSS-Protection "1; mode=block"; + add_header Referrer-Policy "strict-origin-when-cross-origin"; + } + + # API endpoints - no caching + location ~* ^/api/ { + expires -1; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Pragma "no-cache"; + } + + # Production security headers + nginx.ingress.kubernetes.io/configuration-snippet: | + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' https://fonts.gstatic.com https://cdnjs.cloudflare.com; img-src 'self' data: https: blob:; connect-src 'self' https://www.google-analytics.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), speaker=(), fullscreen=(self)" always; + + # Production rate limiting + nginx.ingress.kubernetes.io/rate-limit: "200" + nginx.ingress.kubernetes.io/rate-limit-window: "1m" + nginx.ingress.kubernetes.io/rate-limit-connections: "50" + + # Production monitoring + nginx.ingress.kubernetes.io/enable-access-log: "true" + nginx.ingress.kubernetes.io/enable-rewrite-log: "false" + + # Production redirect from www to non-www + nginx.ingress.kubernetes.io/from-to-www-redirect: "true" +spec: + tls: + - hosts: + - lions.dev + - www.lions.dev + secretName: lionsdev-client-tls-prod + rules: + - host: lions.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: lionsdev-client-service + port: + number: 80 + - host: www.lions.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: lionsdev-client-service + port: + number: 80 diff --git a/applications/lionsdev-client/kubernetes/configmap.yaml b/applications/lionsdev-client/kubernetes/configmap.yaml new file mode 100644 index 0000000..975ad38 --- /dev/null +++ b/applications/lionsdev-client/kubernetes/configmap.yaml @@ -0,0 +1,85 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: lionsdev-client-config + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl +data: + # Application configuration + application.properties: | + # Quarkus Configuration + quarkus.application.name=lionsdev-client + quarkus.application.version=1.0.0 + + # HTTP Configuration + quarkus.http.port=8080 + quarkus.http.host=0.0.0.0 + quarkus.http.cors=true + quarkus.http.cors.origins=https://lions.dev,https://staging.lions.dev,https://dev.lions.dev + + # Database Configuration (will be overridden by secrets) + quarkus.datasource.db-kind=postgresql + quarkus.datasource.jdbc.url=jdbc:postgresql://postgresql-service.postgresql.svc.cluster.local:5432/lionsdb + quarkus.datasource.jdbc.max-size=20 + quarkus.datasource.jdbc.min-size=5 + + # Hibernate Configuration + quarkus.hibernate-orm.database.generation=update + quarkus.hibernate-orm.log.sql=false + quarkus.hibernate-orm.sql-load-script=no-file + + # Logging Configuration + quarkus.log.level=INFO + quarkus.log.category."dev.lions".level=DEBUG + quarkus.log.console.enable=true + quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n + + # Health Check Configuration + quarkus.health.extensions.enabled=true + quarkus.smallrye-health.root-path=/health + + # Metrics Configuration + quarkus.micrometer.enabled=true + quarkus.micrometer.export.prometheus.enabled=true + quarkus.micrometer.export.prometheus.path=/metrics + + # OpenAPI Configuration + quarkus.swagger-ui.enable=true + quarkus.swagger-ui.path=/swagger-ui + mp.openapi.extensions.smallrye.info.title=Lions Dev Client API + mp.openapi.extensions.smallrye.info.version=1.0.0 + mp.openapi.extensions.smallrye.info.description=Lions Dev professional website and client portal + mp.openapi.extensions.smallrye.info.contact.email=contact@lions.dev + mp.openapi.extensions.smallrye.info.contact.name=Lions Dev Team + mp.openapi.extensions.smallrye.info.contact.url=https://lions.dev + + # Security Configuration + quarkus.http.auth.basic=false + quarkus.security.jpa.enabled=false + + # Static Resources Configuration + quarkus.http.static-resources."/"=META-INF/resources + quarkus.http.static-resources.cache-control=max-age=86400 + + # Environment-specific overrides (will be patched per environment) + environment: "production" + domain: "lions.dev" + log-level: "INFO" + + # Lions Dev specific configuration + lions.dev.company.name=Lions Dev + lions.dev.company.email=contact@lions.dev + lions.dev.company.phone=+225 01 01 75 95 25 + lions.dev.company.address=Abidjan, CΓ΄te d'Ivoire + lions.dev.company.website=https://lions.dev + lions.dev.company.description=Enterprise digital transformation partner delivering mission-critical solutions across Africa + + # Feature flags + lions.dev.features.analytics.enabled=true + lions.dev.features.monitoring.enabled=true + lions.dev.features.contact-form.enabled=true + lions.dev.features.newsletter.enabled=true diff --git a/applications/lionsdev-client/kubernetes/deployment.yaml b/applications/lionsdev-client/kubernetes/deployment.yaml new file mode 100644 index 0000000..ea01cfd --- /dev/null +++ b/applications/lionsdev-client/kubernetes/deployment.yaml @@ -0,0 +1,149 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lionsdev-client + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl + app.kubernetes.io/version: "1.0.0" + annotations: + deployment.kubernetes.io/revision: "1" + description: "Lions Dev professional website and client portal" +spec: + replicas: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + selector: + matchLabels: + app: lionsdev-client + template: + metadata: + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/version: "1.0.0" + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/metrics" + spec: + serviceAccountName: lionsdev-client + securityContext: + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + containers: + - name: lionsdev-client + image: registry.lions.dev/lionsdev/lionsdev-client:latest + imagePullPolicy: Always + ports: + - name: http + containerPort: 8080 + protocol: TCP + - name: metrics + containerPort: 8080 + protocol: TCP + env: + - name: KUBERNETES_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: HOSTNAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: QUARKUS_PROFILE + value: "prod" + envFrom: + - configMapRef: + name: lionsdev-client-config + - secretRef: + name: lionsdev-client-secrets + resources: + requests: + memory: "256Mi" + cpu: "200m" + limits: + memory: "1Gi" + cpu: "1000m" + livenessProbe: + httpGet: + path: /health/live + port: http + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + readinessProbe: + httpGet: + path: /health/ready + port: http + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + successThreshold: 1 + startupProbe: + httpGet: + path: /health/started + port: http + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 30 + successThreshold: 1 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1001 + capabilities: + drop: + - ALL + volumeMounts: + - name: tmp + mountPath: /tmp + - name: logs + mountPath: /app/logs + volumes: + - name: tmp + emptyDir: {} + - name: logs + emptyDir: {} + imagePullSecrets: + - name: registry-lions-dev + nodeSelector: + kubernetes.io/os: linux + tolerations: + - key: "node.kubernetes.io/not-ready" + operator: "Exists" + effect: "NoExecute" + tolerationSeconds: 300 + - key: "node.kubernetes.io/unreachable" + operator: "Exists" + effect: "NoExecute" + tolerationSeconds: 300 + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - lionsdev-client + topologyKey: kubernetes.io/hostname diff --git a/applications/lionsdev-client/kubernetes/hpa.yaml b/applications/lionsdev-client/kubernetes/hpa.yaml new file mode 100644 index 0000000..6640e05 --- /dev/null +++ b/applications/lionsdev-client/kubernetes/hpa.yaml @@ -0,0 +1,67 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: lionsdev-client-hpa + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: lionsdev-client + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 50 + periodSeconds: 60 + - type: Pods + value: 2 + periodSeconds: 60 + selectPolicy: Min + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Percent + value: 100 + periodSeconds: 30 + - type: Pods + value: 4 + periodSeconds: 30 + selectPolicy: Max +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: lionsdev-client-pdb + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl +spec: + minAvailable: 1 + selector: + matchLabels: + app: lionsdev-client diff --git a/applications/lionsdev-client/kubernetes/ingress.yaml b/applications/lionsdev-client/kubernetes/ingress.yaml new file mode 100644 index 0000000..f019eb7 --- /dev/null +++ b/applications/lionsdev-client/kubernetes/ingress.yaml @@ -0,0 +1,89 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: lionsdev-client-ingress + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl + annotations: + # Nginx Ingress Controller annotations + kubernetes.io/ingress.class: "nginx" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/use-regex: "true" + + # SSL/TLS Configuration + cert-manager.io/cluster-issuer: "letsencrypt-prod" + cert-manager.io/acme-challenge-type: "http01" + + # Performance and caching + nginx.ingress.kubernetes.io/proxy-body-size: "10m" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" + nginx.ingress.kubernetes.io/proxy-send-timeout: "60" + nginx.ingress.kubernetes.io/proxy-read-timeout: "60" + nginx.ingress.kubernetes.io/proxy-buffering: "on" + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" + + # Static assets caching + nginx.ingress.kubernetes.io/server-snippet: | + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + add_header X-Content-Type-Options "nosniff"; + } + + location ~* \.(html|htm)$ { + expires 1h; + add_header Cache-Control "public, must-revalidate"; + add_header X-Content-Type-Options "nosniff"; + add_header X-Frame-Options "DENY"; + add_header X-XSS-Protection "1; mode=block"; + add_header Referrer-Policy "strict-origin-when-cross-origin"; + } + + # Security headers + nginx.ingress.kubernetes.io/configuration-snippet: | + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' https://fonts.gstatic.com https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none';" always; + + # Rate limiting + nginx.ingress.kubernetes.io/rate-limit: "100" + nginx.ingress.kubernetes.io/rate-limit-window: "1m" + + # Monitoring + nginx.ingress.kubernetes.io/enable-access-log: "true" + nginx.ingress.kubernetes.io/enable-rewrite-log: "false" +spec: + tls: + - hosts: + - lions.dev + - www.lions.dev + secretName: lionsdev-client-tls + rules: + - host: lions.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: lionsdev-client-service + port: + number: 80 + - host: www.lions.dev + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: lionsdev-client-service + port: + number: 80 diff --git a/applications/lionsdev-client/kubernetes/namespace.yaml b/applications/lionsdev-client/kubernetes/namespace.yaml new file mode 100644 index 0000000..3b5e719 --- /dev/null +++ b/applications/lionsdev-client/kubernetes/namespace.yaml @@ -0,0 +1,47 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: lions-apps + labels: + name: lions-apps + app.kubernetes.io/name: lions-apps + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl + annotations: + description: "Lions Dev applications namespace" + contact: "infrastructure@lions.dev" + environment: "multi-environment" +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: lions-apps-quota + namespace: lions-apps +spec: + hard: + requests.cpu: "4" + requests.memory: 8Gi + limits.cpu: "8" + limits.memory: 16Gi + persistentvolumeclaims: "10" + services: "20" + secrets: "50" + configmaps: "50" +--- +apiVersion: v1 +kind: LimitRange +metadata: + name: lions-apps-limits + namespace: lions-apps +spec: + limits: + - default: + cpu: "1" + memory: "1Gi" + defaultRequest: + cpu: "100m" + memory: "128Mi" + type: Container + - default: + storage: "10Gi" + type: PersistentVolumeClaim diff --git a/applications/lionsdev-client/kubernetes/rbac.yaml b/applications/lionsdev-client/kubernetes/rbac.yaml new file mode 100644 index 0000000..d7a0cfd --- /dev/null +++ b/applications/lionsdev-client/kubernetes/rbac.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: lionsdev-client + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl +automountServiceAccountToken: true +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: lionsdev-client-role + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl +rules: +- apiGroups: [""] + resources: ["configmaps", "secrets"] + verbs: ["get", "list", "watch"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] +- apiGroups: ["apps"] + resources: ["deployments", "replicasets"] + verbs: ["get", "list", "watch"] +- apiGroups: [""] + resources: ["services", "endpoints"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: lionsdev-client-rolebinding + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl +subjects: +- kind: ServiceAccount + name: lionsdev-client + namespace: lions-apps +roleRef: + kind: Role + name: lionsdev-client-role + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: Secret +metadata: + name: registry-lions-dev + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5saW9ucy5kZXYiOnsidXNlcm5hbWUiOiJsaW9uc3JlZ2lzdHJ5IiwicGFzc3dvcmQiOiJMaW9uc1JlZ2lzdHJ5MjAyNSEiLCJhdXRoIjoiYkdsdmJuTnlaV2RwYzNSeWVUcE1hVzl1YzFKbFoybHpkSEo1TWpBeU5TRT0ifX19 + # Base64 encoded Docker config for registry.lions.dev + # Username: lionsregistry + # Password: LionsRegistry2025! diff --git a/applications/lionsdev-client/kubernetes/secret.yaml b/applications/lionsdev-client/kubernetes/secret.yaml new file mode 100644 index 0000000..da66208 --- /dev/null +++ b/applications/lionsdev-client/kubernetes/secret.yaml @@ -0,0 +1,61 @@ +apiVersion: v1 +kind: Secret +metadata: + name: lionsdev-client-secrets + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/role: "lionsdev-client" + vault.hashicorp.com/agent-inject-secret-database: "secret/lionsdev-client/database" + vault.hashicorp.com/agent-inject-template-database: | + {{- with secret "secret/lionsdev-client/database" -}} + QUARKUS_DATASOURCE_USERNAME={{ .Data.data.username }} + QUARKUS_DATASOURCE_PASSWORD={{ .Data.data.password }} + {{- end }} +type: Opaque +data: + # Database credentials (base64 encoded) + # These will be injected by Vault in production + # Default values for development/testing + QUARKUS_DATASOURCE_USERNAME: bGlvbnNfdXNlcg== # lions_user + QUARKUS_DATASOURCE_PASSWORD: TGlvbnNEZXYyMDI1IQ== # LionsDev2025! + + # SMTP Configuration for contact forms + QUARKUS_MAILER_HOST: c210cC5nbWFpbC5jb20= # smtp.gmail.com + QUARKUS_MAILER_PORT: NTg3 # 587 + QUARKUS_MAILER_USERNAME: Y29udGFjdEBsaW9ucy5kZXY= # contact@lions.dev + QUARKUS_MAILER_PASSWORD: "" # Will be injected by Vault + + # JWT Secret for session management + JWT_SECRET: TGlvbnNEZXZKV1RTZWNyZXQyMDI1IUVudGVycHJpc2U= # LionsDevJWTSecret2025!Enterprise + + # API Keys for external services + GOOGLE_ANALYTICS_ID: "" # Will be injected by Vault + GOOGLE_MAPS_API_KEY: "" # Will be injected by Vault + + # Monitoring and observability + PROMETHEUS_AUTH_TOKEN: "" # Will be injected by Vault + GRAFANA_API_KEY: "" # Will be injected by Vault +--- +apiVersion: v1 +kind: Secret +metadata: + name: lionsdev-client-tls + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl + annotations: + cert-manager.io/issuer: "letsencrypt-prod" +type: kubernetes.io/tls +data: + # TLS certificate and key will be automatically generated by cert-manager + tls.crt: "" + tls.key: "" diff --git a/applications/lionsdev-client/kubernetes/service.yaml b/applications/lionsdev-client/kubernetes/service.yaml new file mode 100644 index 0000000..116e04d --- /dev/null +++ b/applications/lionsdev-client/kubernetes/service.yaml @@ -0,0 +1,53 @@ +apiVersion: v1 +kind: Service +metadata: + name: lionsdev-client-service + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: "nlb" + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/metrics" +spec: + type: ClusterIP + ports: + - name: http + port: 80 + targetPort: http + protocol: TCP + - name: metrics + port: 8080 + targetPort: metrics + protocol: TCP + selector: + app: lionsdev-client + sessionAffinity: None +--- +apiVersion: v1 +kind: Service +metadata: + name: lionsdev-client-headless + namespace: lions-apps + labels: + app: lionsdev-client + app.kubernetes.io/name: lionsdev-client + app.kubernetes.io/part-of: lions-infrastructure + app.kubernetes.io/managed-by: lionsctl + annotations: + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" +spec: + type: ClusterIP + clusterIP: None + ports: + - name: http + port: 80 + targetPort: http + protocol: TCP + selector: + app: lionsdev-client + publishNotReadyAddresses: true diff --git a/deploy-lionsdev-client.sh b/deploy-lionsdev-client.sh new file mode 100644 index 0000000..b662527 --- /dev/null +++ b/deploy-lionsdev-client.sh @@ -0,0 +1,212 @@ +#!/bin/bash + +# Lions Dev Client Deployment Script +# This script deploys the Lions Dev application using lionsctl pipeline + +set -euo pipefail + +# Configuration +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIONSCTL_PATH="${SCRIPT_DIR}/../lions-infrastructure-2025/lionsctl/lionsctl.exe" +REPO_URL="https://git.lions.dev/lionsctl-bot/lionsctl-deployments" +BRANCH="main" +JAVA_VERSION=17 +NOTIFICATION_EMAIL="gbanedahoud@gmail.com" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Help function +show_help() { + cat << EOF +Lions Dev Client Deployment Script + +Usage: $0 [OPTIONS] + +OPTIONS: + -e, --environment Target environment (dev|staging|production) [default: production] + -c, --cluster Target cluster (k1|k2) [default: k2 for production, k1 for dev/staging] + -b, --branch Git branch to deploy [default: main] + -m, --email Notification email [default: gbanedahoud@gmail.com] + -j, --java-version Java version to use [default: 17] + -h, --help Show this help message + +EXAMPLES: + # Deploy to production (default) + $0 + + # Deploy to staging + $0 --environment staging + + # Deploy to development + $0 --environment dev --cluster k1 + + # Deploy specific branch to production + $0 --branch feature/new-design --environment production + +ENVIRONMENTS: + - dev: Development environment on k1 cluster (dev.lions.dev) + - staging: Staging environment on k1 cluster (staging.lions.dev) + - production: Production environment on k2 cluster (lions.dev) + +EOF +} + +# Parse command line arguments +ENVIRONMENT="production" +CLUSTER="" +EMAIL="$NOTIFICATION_EMAIL" + +while [[ $# -gt 0 ]]; do + case $1 in + -e|--environment) + ENVIRONMENT="$2" + shift 2 + ;; + -c|--cluster) + CLUSTER="$2" + shift 2 + ;; + -b|--branch) + BRANCH="$2" + shift 2 + ;; + -m|--email) + EMAIL="$2" + shift 2 + ;; + -j|--java-version) + JAVA_VERSION="$2" + shift 2 + ;; + -h|--help) + show_help + exit 0 + ;; + *) + log_error "Unknown option: $1" + show_help + exit 1 + ;; + esac +done + +# Set default cluster based on environment if not specified +if [[ -z "$CLUSTER" ]]; then + case $ENVIRONMENT in + production) + CLUSTER="k2" + ;; + dev|staging) + CLUSTER="k1" + ;; + *) + log_error "Invalid environment: $ENVIRONMENT. Must be dev, staging, or production." + exit 1 + ;; + esac +fi + +# Validate inputs +if [[ ! "$ENVIRONMENT" =~ ^(dev|staging|production)$ ]]; then + log_error "Invalid environment: $ENVIRONMENT. Must be dev, staging, or production." + exit 1 +fi + +if [[ ! "$CLUSTER" =~ ^(k1|k2)$ ]]; then + log_error "Invalid cluster: $CLUSTER. Must be k1 or k2." + exit 1 +fi + +# Check if lionsctl exists +if [[ ! -f "$LIONSCTL_PATH" ]]; then + log_error "lionsctl not found at: $LIONSCTL_PATH" + log_info "Please build lionsctl first:" + log_info " cd ../lions-infrastructure-2025/lionsctl && go build -o lionsctl.exe" + exit 1 +fi + +# Display deployment information +log_info "πŸš€ Lions Dev Client Deployment" +log_info "================================" +log_info "Repository: $REPO_URL" +log_info "Branch: $BRANCH" +log_info "Environment: $ENVIRONMENT" +log_info "Cluster: $CLUSTER" +log_info "Java Version: $JAVA_VERSION" +log_info "Email: $EMAIL" +log_info "================================" + +# Confirm deployment +if [[ "$ENVIRONMENT" == "production" ]]; then + log_warning "⚠️ You are about to deploy to PRODUCTION!" + log_warning "This will affect the live Lions Dev website at https://lions.dev" + read -p "Are you sure you want to continue? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_info "Deployment cancelled." + exit 0 + fi +fi + +# Execute deployment +log_info "πŸ”„ Starting deployment..." + +"$LIONSCTL_PATH" pipeline \ + --url "$REPO_URL" \ + --branch "$BRANCH" \ + --java-version "$JAVA_VERSION" \ + --environment "$ENVIRONMENT" \ + --cluster "$CLUSTER" \ + --mail "$EMAIL" \ + --verbose + +DEPLOYMENT_STATUS=$? + +if [[ $DEPLOYMENT_STATUS -eq 0 ]]; then + log_success "πŸŽ‰ Deployment completed successfully!" + + case $ENVIRONMENT in + production) + log_success "βœ… Lions Dev is now live at: https://lions.dev" + ;; + staging) + log_success "βœ… Staging environment is available at: https://staging.lions.dev" + ;; + dev) + log_success "βœ… Development environment is available at: https://dev.lions.dev" + ;; + esac + + log_info "πŸ“Š You can monitor the deployment at:" + log_info " - Kubernetes Dashboard: https://k8s.lions.dev" + log_info " - Grafana Monitoring: https://grafana.lions.dev" + log_info " - Application Logs: kubectl logs -n lions-apps -l app=lionsdev-client" + +else + log_error "❌ Deployment failed with exit code: $DEPLOYMENT_STATUS" + log_info "πŸ” Check the logs above for error details" + log_info "πŸ“§ A notification email has been sent to: $EMAIL" + exit $DEPLOYMENT_STATUS +fi