--- # OpenSearch StatefulSet for lexical (BM25) search # Deployed alongside pgvector for hybrid semantic+lexical search # JWT realm configured for Authentik integration apiVersion: v1 kind: Namespace metadata: name: poimen labels: pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/audit: baseline pod-security.kubernetes.io/warn: baseline --- # OpenSearch Service (Headless for StatefulSet discovery) apiVersion: v1 kind: Service metadata: name: opensearch namespace: poimen labels: app.kubernetes.io/name: opensearch spec: clusterIP: None # Headless selector: app.kubernetes.io/name: opensearch ports: - name: http port: 9200 targetPort: 9200 - name: transport port: 9300 targetPort: 9300 --- # OpenSearch Service (Internal for queries from Memory Service) apiVersion: v1 kind: Service metadata: name: opensearch-internal namespace: poimen labels: app.kubernetes.io/name: opensearch spec: type: ClusterIP selector: app.kubernetes.io/name: opensearch ports: - name: http port: 9200 targetPort: 9200 --- # ConfigMap: OpenSearch configuration with JWT realm apiVersion: v1 kind: ConfigMap metadata: name: opensearch-config namespace: poimen data: opensearch.yml: | # Cluster settings cluster.name: poimen-memory node.name: ${HOSTNAME} cluster.initial_master_nodes: opensearch-0 discovery.seed_hosts: opensearch-0.opensearch.poimen.svc.cluster.local # Network network.host: 0.0.0.0 http.port: 9200 transport.port: 9300 # Security Plugin (JWT/Authentik OIDC) plugins.security.disabled: "false" plugins.security.ssl.http.enabled: "false" plugins.security.ssl.transport.enabled: "false" # JWT Authentication Realm for Authentik plugins.security.authcz.admin_dn: - "CN=admin,OU=admin,O=admin,L=admin,ST=admin,C=admin" plugins.security.authc.realms.jwt_realm.type: jwt plugins.security.authc.realms.jwt_realm.order: 1 plugins.security.authc.realms.jwt_realm.http_enabled: true plugins.security.authc.realms.jwt_realm.transport_enabled: false plugins.security.authc.realms.jwt_realm.description: "JWT realm for Authentik OIDC" plugins.security.authc.realms.jwt_realm.token_name: Authorization plugins.security.authc.realms.jwt_realm.token_extractor: "Bearer " plugins.security.authc.realms.jwt_realm.jwt_header: Authorization plugins.security.authc.realms.jwt_realm.roles_key: roles plugins.security.authc.realms.jwt_realm.subject_key: sub plugins.security.authc.realms.jwt_realm.jwks_uri: "https://authentik.riotpiao.com/application/o/poimen-memory/jwks/" plugins.security.authc.realms.jwt_realm.jwks_refresh_interval_ms: 3600000 plugins.security.authc.realms.jwt_realm.issuer: "https://authentik.riotpiao.com/application/o/poimen-memory/" plugins.security.authc.realms.jwt_realm.enable_ssl_peer_hostname_verification: false plugins.security.authc.realms.jwt_realm.skip_jwt_verification: false plugins.security.authc.cache.enable: true plugins.security.authc.backends.internal_authc_backend.type: intern # Memory indices.memory.index_buffer_size: 30% log4j2.properties: | status = warn appender.console.type = Console appender.console.name = console appender.console.layout.type = PatternLayout appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n rootLogger.level = info rootLogger.appenderRef.console.ref = console internal_users.yml: | admin: hash: "$2y$12$K/SpwjtB.wW8u3/52l.f2OPST9/PgBkqquzi.Oi8KfRMfsKkCq3GO" reserved: true backend_roles: - "admin" - "all_access" roles_mapping.yml: | all_access: reserved: false backend_roles: - "*" hosts: - "*" --- # StatefulSet: OpenSearch (2 replicas for HA cluster) apiVersion: apps/v1 kind: StatefulSet metadata: name: opensearch namespace: poimen labels: app.kubernetes.io/name: opensearch spec: serviceName: opensearch replicas: 1 selector: matchLabels: app.kubernetes.io/name: opensearch template: metadata: labels: app.kubernetes.io/name: opensearch spec: serviceAccountName: opensearch hostNetwork: false initContainers: - name: sysctl image: busybox:1.28 command: - sysctl - -w - vm.max_map_count=262144 securityContext: privileged: true containers: - name: opensearch image: opensearchproject/opensearch:2.11.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 9200 - name: transport containerPort: 9300 env: - name: HOSTNAME valueFrom: fieldRef: fieldPath: metadata.name - name: CLUSTER_NAME value: "poimen-memory" - name: OPENSEARCH_JAVA_OPTS value: "-Xms1g -Xmx1g" - name: DISABLE_SECURITY_PLUGIN value: "false" - name: OPENSEARCH_INITIAL_ADMIN_PASSWORD valueFrom: secretKeyRef: name: opensearch-secrets key: admin-password # Volume mounts volumeMounts: - name: opensearch-data mountPath: /usr/share/opensearch/data - name: opensearch-config mountPath: /usr/share/opensearch/config/opensearch.yml subPath: opensearch.yml - name: opensearch-config mountPath: /usr/share/opensearch/plugins/opensearch-security/securityconfig/internal_users.yml subPath: internal_users.yml - name: opensearch-config mountPath: /usr/share/opensearch/plugins/opensearch-security/securityconfig/roles_mapping.yml subPath: roles_mapping.yml - name: opensearch-logs mountPath: /usr/share/opensearch/logs # Resource limits resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1000m" # Liveness probe (skip auth via basic fallback) livenessProbe: httpGet: path: /_cluster/health port: 9200 httpHeaders: - name: Authorization value: Basic YWRtaW46YWRtaW4=" initialDelaySeconds: 60 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 # Readiness probe readinessProbe: httpGet: path: /_cluster/health?local=true port: 9200 httpHeaders: - name: Authorization value: Basic YWRtaW46YWRtaW4=" initialDelaySeconds: 30 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 2 # Security context securityContext: runAsUser: 1000 # Volumes volumes: - name: opensearch-config configMap: name: opensearch-config - name: opensearch-logs emptyDir: {} # PVC template for data persistence volumeClaimTemplates: - metadata: name: opensearch-data spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 30Gi --- # ServiceAccount for OpenSearch apiVersion: v1 kind: ServiceAccount metadata: name: opensearch namespace: poimen --- # NetworkPolicy: Only Memory Service can access OpenSearch apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: opensearch-access namespace: poimen spec: podSelector: matchLabels: app.kubernetes.io/name: opensearch policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app.kubernetes.io/name: poimen-memory - podSelector: matchLabels: app.kubernetes.io/name: opensearch-dashboards ports: - protocol: TCP port: 9200 --- # OpenSearch Dashboards: UI for monitoring, querying, and managing OpenSearch apiVersion: v1 kind: Service metadata: name: opensearch-dashboards namespace: poimen labels: app.kubernetes.io/name: opensearch-dashboards spec: type: ClusterIP selector: app.kubernetes.io/name: opensearch-dashboards ports: - name: http port: 5601 targetPort: 5601 --- # ConfigMap: OpenSearch Dashboards configuration apiVersion: v1 kind: ConfigMap metadata: name: opensearch-dashboards-config namespace: poimen data: opensearch_dashboards.yml: | # OpenSearch Dashboards configuration server.name: opensearch-dashboards server.host: "0.0.0.0" server.port: 5601 # OpenSearch connection opensearch.hosts: ["http://opensearch-internal.poimen.svc.cluster.local:9200"] opensearch.username: "admin" opensearch.password: "admin" opensearch.ssl.verificationMode: none # Logging logging.appenders.default.type: console logging.appenders.default.layout.type: pattern logging.appenders.default.layout.pattern: "[%date][%level][%logger] %message" logging.root.appenders: [default] logging.root.level: info --- # Deployment: OpenSearch Dashboards apiVersion: apps/v1 kind: Deployment metadata: name: opensearch-dashboards namespace: poimen labels: app.kubernetes.io/name: opensearch-dashboards spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: opensearch-dashboards template: metadata: labels: app.kubernetes.io/name: opensearch-dashboards spec: serviceAccountName: opensearch-dashboards containers: - name: opensearch-dashboards image: opensearchproject/opensearch-dashboards:2.11.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 5601 env: - name: OPENSEARCH_HOSTS value: "http://opensearch-internal.poimen.svc.cluster.local:9200" - name: OPENSEARCH_USERNAME value: "admin" - name: OPENSEARCH_PASSWORD valueFrom: secretKeyRef: name: opensearch-dashboards-secret key: password # Volume mounts volumeMounts: - name: opensearch-dashboards-config mountPath: /usr/share/opensearch-dashboards/config/opensearch_dashboards.yml subPath: opensearch_dashboards.yml # Resource limits resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" # Liveness probe livenessProbe: httpGet: path: / port: 5601 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 # Readiness probe readinessProbe: httpGet: path: / port: 5601 initialDelaySeconds: 15 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 2 # Security context securityContext: runAsNonRoot: true runAsUser: 1000 capabilities: drop: - ALL # Volumes volumes: - name: opensearch-dashboards-config configMap: name: opensearch-dashboards-config --- # Secret: OpenSearch Dashboards password apiVersion: v1 kind: Secret metadata: name: opensearch-dashboards-secret namespace: poimen type: Opaque stringData: password: "admin" # ⚠️ Change in production --- # ServiceAccount for OpenSearch Dashboards apiVersion: v1 kind: ServiceAccount metadata: name: opensearch-dashboards namespace: poimen --- # Secret for OpenSearch Admin Password apiVersion: v1 kind: Secret metadata: name: opensearch-secrets namespace: poimen type: Opaque stringData: admin-password: "OpenSearch@Admin123!"