commit 0ea4c0af81b94a57e6aa1dcc10400afa4f4a6f68 Author: Raiko Oll Date: Tue Feb 10 13:14:26 2026 +0200 Init upload diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..2fee45a --- /dev/null +++ b/app.yaml @@ -0,0 +1,489 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: memelord-settings + namespace: memelord-raiko +data: + settings.py: | + """ + Django settings for myproject project. + """ + from pathlib import Path + from dotenv import load_dotenv + import os + import pytz + import secrets + from django.utils.html import escape + from django.utils.translation import gettext_lazy as _ + from csp.constants import NONE, SELF, UNSAFE_INLINE, UNSAFE_EVAL + + load_dotenv() + + BASE_DIR = Path(__file__).resolve().parent.parent + DEBUG = os.environ.get('DEBUG', 'False').lower() in ['true'] + VERSION = escape(os.environ.get("VERSION", '')) + ENABLE_PUBLIC_FEED = os.environ.get('ENABLE_PUBLIC_FEED', 'False').lower() in ['true', '1', 'yes'] + SECRET_KEY = os.environ.get("SECRET_KEY", secrets.token_urlsafe(32)) + + DOMAIN = "" + ALLOWED_HOSTS = ["127.0.0.1", "localhost", "10.*", "172.*", "192.168.*"] + CSRF_TRUSTED_ORIGINS = [] + + DOMAIN = str(os.environ.get("DOMAIN", "localhost")) + TRUSTED_PORT = str(os.environ.get("PORT", "8000")) + + if DOMAIN: + domains = DOMAIN.split(',') + for domain in domains: + domain = domain.strip().rstrip('/').replace('http://', '').replace('https://', '') + if domain: + ALLOWED_HOSTS.append(domain) + CSRF_TRUSTED_ORIGINS.append(f"https://{domain}") + if TRUSTED_PORT not in ["80", "443", "8000"]: + CSRF_TRUSTED_ORIGINS.append(f"https://{domain}:{TRUSTED_PORT}") + + CSRF_COOKIE_HTTPONLY = True + SESSION_EXPIRE_AT_BROWSER_CLOSE = os.environ.get('SESSION_EXPIRE_AT_BROWSER_CLOSE', 'True').lower() in ['true'] + SESSION_COOKIE_AGE = int(os.environ.get('SESSION_COOKIE_AGE', '30')) * 60 + SESSION_COOKIE_NAME = 'Session' + SESSION_COOKIE_SAMESITE = 'Lax' + + REDIS_HOST = os.environ.get("REDIS_HOST", "") + REDIS_PORT = os.environ.get("REDIS_PORT", "6379") + REDIS_DB = os.environ.get("REDIS_DB", "0") + REDIS_PASSWORD = os.environ.get("REDIS_PASSWORD", "") + + if REDIS_HOST: + CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "PASSWORD": REDIS_PASSWORD if REDIS_PASSWORD else None, + "SOCKET_CONNECT_TIMEOUT": 5, + "SOCKET_TIMEOUT": 5, + "RETRY_ON_TIMEOUT": True, + "CONNECTION_POOL_KWARGS": { + "max_connections": 50, + "retry_on_timeout": True, + }, + "COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", + "IGNORE_EXCEPTIONS": True, + }, + "KEY_PREFIX": "memelord", + "TIMEOUT": 300, + } + } + SESSION_ENGINE = "django.contrib.sessions.backends.cache" + SESSION_CACHE_ALIAS = "default" + else: + SESSION_ENGINE = "django.contrib.sessions.backends.db" + + SECURE_COOKIES = os.environ.get('SECURE_COOKIES', 'False').lower() in ['true'] + + if SECURE_COOKIES: + SESSION_COOKIE_SECURE = True + CSRF_COOKIE_SECURE = True + SECURE_HSTS_SECONDS = "31536000" + SECURE_HSTS_PRELOAD = True + SECURE_HSTS_INCLUDE_SUBDOMAINS = True + else: + SESSION_COOKIE_SECURE = False + CSRF_COOKIE_SECURE = False + + SECURE_BROWSER_XSS_FILTER = True + SECURE_CONTENT_TYPE_NOSNIFF = True + X_FRAME_OPTIONS = 'DENY' + REFERRER_POLICY = 'same-origin' + + raw_frame_ancestors = os.environ.get("CSP_FRAME_ANCESTORS", "'none'") + FRAME_ANCESTORS = [item.strip() for item in raw_frame_ancestors.split(',') if item.strip()] + + STORAGE_BACKEND = os.environ.get('STORAGE_BACKEND', 'local').lower() + IMG_SRC_LIST = ["'self'", "data:", "blob:", "https://img.logo.dev"] + + if STORAGE_BACKEND == 's3': + AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') + AWS_S3_CUSTOM_DOMAIN = os.environ.get('AWS_S3_CUSTOM_DOMAIN') + AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'us-east-1') + AWS_S3_ENDPOINT_URL = os.environ.get('AWS_S3_ENDPOINT_URL') + AWS_S3_USE_ACCELERATE_ENDPOINT = os.environ.get('AWS_S3_USE_ACCELERATE_ENDPOINT', 'False').lower() in ['true'] + + if AWS_S3_CUSTOM_DOMAIN: + IMG_SRC_LIST.append(f"https://{AWS_S3_CUSTOM_DOMAIN}") + + if AWS_S3_ENDPOINT_URL: + from urllib.parse import urlparse + parsed_url = urlparse(AWS_S3_ENDPOINT_URL) + endpoint_domain = parsed_url.netloc + endpoint_scheme = parsed_url.scheme or 'https' + IMG_SRC_LIST.append(f"{endpoint_scheme}://{endpoint_domain}") + if endpoint_domain and AWS_STORAGE_BUCKET_NAME: + IMG_SRC_LIST.append(f"{endpoint_scheme}://{AWS_STORAGE_BUCKET_NAME}.{endpoint_domain}") + else: + if AWS_STORAGE_BUCKET_NAME: + IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com") + IMG_SRC_LIST.append(f"https://{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com") + + CSP_IMG_SRC_EXTRA = os.environ.get('CSP_IMG_SRC_EXTRA', '') + if CSP_IMG_SRC_EXTRA: + extra_domains = [domain.strip() for domain in CSP_IMG_SRC_EXTRA.split(',') if domain.strip()] + IMG_SRC_LIST.extend(extra_domains) + + CONTENT_SECURITY_POLICY = { + "DIRECTIVES": { + "default-src": ["'self'"], + "style-src": ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdn.jsdelivr.net"], + "script-src": ["'self'", "'unsafe-inline'", "'unsafe-eval'", "https://cdn.jsdelivr.net"], + "font-src": ["'self'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"], + "img-src": IMG_SRC_LIST, + "object-src": ["'none'"], + "connect-src": ["'self'"], + "frame-ancestors": FRAME_ANCESTORS, + }, + } + + INSTALLED_APPS = [ + 'myapp', + 'django_celery_beat', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'csp', + 'storages', + ] + + MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django_http_referrer_policy.middleware.ReferrerPolicyMiddleware', + 'csp.middleware.CSPMiddleware', + ] + + ROOT_URLCONF = 'myproject.urls' + + TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'myapp/templates/registration')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, + ] + + DB_ENGINE = os.environ.get("DB_ENGINE", "sqlite3") + + if DB_ENGINE == "sqlite3": + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'database', 'db.sqlite3'), + } + } + elif DB_ENGINE == "postgres": + DB_HOST = os.environ.get("POSTGRES_HOST", "db") + DB_PORT = os.environ.get("POSTGRES_PORT", "5432") + DB_USER = os.environ.get("POSTGRES_USER", "memelord") + DB_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "memelord") + DB_NAME = os.environ.get("POSTGRES_DB", "memelord") + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': DB_NAME, + 'HOST': DB_HOST, + 'PORT': DB_PORT, + 'USER': DB_USER, + 'PASSWORD': DB_PASSWORD, + } + } + + AUTH_PASSWORD_VALIDATORS = [ + {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, + {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, + {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, + {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, + ] + + LANGUAGE_CODE = 'en-us' + TIME_ZONE = os.environ.get('TZ', 'Europe/Berlin') + USE_I18N = True + USE_L10N = True + USE_TZ = True + + LANGUAGES = [ + ('en', _('English')), + ('de', _('German')), + ('fr', _('French')), + ('it', _('Italian')), + ] + + LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')] + + LOGS_DIR = os.path.join(BASE_DIR, 'logs') + + LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': '{levelname} {asctime} {module} {message}', + 'style': '{', + }, + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + }, + }, + 'loggers': { + 'django': { + 'handlers': ['console'], + 'level': os.environ.get('DJANGO_LOG_LEVEL', 'INFO'), + 'propagate': False, + }, + 'myapp': { + 'handlers': ['console'], + 'level': os.environ.get('DJANGO_LOG_LEVEL', 'INFO'), + 'propagate': False, + }, + 'storages': { + 'handlers': ['console'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + 'boto3': { + 'handlers': ['console'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + 'botocore': { + 'handlers': ['console'], + 'level': 'DEBUG' if DEBUG else 'INFO', + 'propagate': False, + }, + }, + 'root': { + 'handlers': ['console'], + 'level': 'INFO', + }, + } + + os.makedirs(LOGS_DIR, exist_ok=True) + + STATIC_URL = '/static/' + STATIC_ROOT = os.path.join(BASE_DIR, 'myapp', 'static') + DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + LOGIN_URL = '/accounts/login/' + LOGIN_REDIRECT_URL = '/' + LOGOUT_REDIRECT_URL = '/post-logout/' + ALLOW_LOGOUT_GET_METHOD = True + WSGI_APPLICATION = 'myproject.wsgi.application' + + OIDC_ENABLED = os.environ.get('OIDC_ENABLED', 'False').lower() in ['true'] + OIDC_AUTOLOGIN = os.environ.get('OIDC_AUTOLOGIN', 'False').lower() in ['true'] + MAX_UPLOAD_SIZE_MB = int(os.environ.get('MAX_UPLOAD_SIZE_MB', '10')) * 1024 * 1024 + + STORAGE_BACKEND = os.environ.get('STORAGE_BACKEND', 'local').lower() + + if STORAGE_BACKEND == 's3': + AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') + AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') + AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') + AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'us-east-1') + AWS_S3_CUSTOM_DOMAIN = os.environ.get('AWS_S3_CUSTOM_DOMAIN') + AWS_S3_ENDPOINT_URL = os.environ.get('AWS_S3_ENDPOINT_URL') + AWS_DEFAULT_ACL = os.environ.get('AWS_DEFAULT_ACL', 'private') + AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'} + AWS_QUERYSTRING_AUTH = os.environ.get('AWS_QUERYSTRING_AUTH', 'True').lower() in ['true'] + AWS_S3_FILE_OVERWRITE = os.environ.get('AWS_S3_FILE_OVERWRITE', 'False').lower() in ['true'] + AWS_LOCATION = os.environ.get('AWS_LOCATION', 'media') + AWS_S3_SIGNATURE_VERSION = 's3v4' + AWS_S3_ADDRESSING_STYLE = 'path' + + STORAGES = { + "default": { + "BACKEND": "storages.backends.s3boto3.S3Boto3Storage", + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, + } + + DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' + + if AWS_S3_CUSTOM_DOMAIN: + MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_LOCATION}/' + elif AWS_STORAGE_BUCKET_NAME: + if AWS_S3_REGION_NAME == 'us-east-1': + MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/{AWS_LOCATION}/' + else: + MEDIA_URL = f'https://{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com/{AWS_LOCATION}/' + else: + MEDIA_URL = "/media/" + + MEDIA_ROOT = BASE_DIR / "media" + else: + STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, + } + DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' + MEDIA_URL = "/media/" + MEDIA_ROOT = BASE_DIR / "media" + + if OIDC_ENABLED: + OIDC_CREATE_USER = os.environ.get('OIDC_CREATE_USER', 'True').lower() in ['true'] + OIDC_RP_SIGN_ALGO = os.environ.get('OIDC_RP_SIGN_ALGO', 'HS256') + OIDC_OP_JWKS_ENDPOINT = os.environ.get('OIDC_OP_JWKS_ENDPOINT') + OIDC_RP_IDP_SIGN_KEY = os.environ.get('OIDC_RP_IDP_SIGN_KEY') + OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID') + OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET') + OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_OP_AUTHORIZATION_ENDPOINT') + OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_OP_TOKEN_ENDPOINT') + OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_OP_USER_ENDPOINT') + OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = float(os.environ.get('OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS', 900)) + OIDC_USERNAME_ALGO = 'myapp.utils.generate_username' + INSTALLED_APPS.append('mozilla_django_oidc') + AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'mozilla_django_oidc.auth.OIDCAuthenticationBackend', + ) + MIDDLEWARE.append('mozilla_django_oidc.middleware.SessionRefresh') + SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: memelord + namespace: memelord-raiko +spec: + replicas: 1 + selector: + matchLabels: + app: memelord + template: + metadata: + labels: + app: memelord + spec: + containers: + - name: memelord + image: ghcr.io/l4rm4nd/memelord:latest + imagePullPolicy: Always + ports: + - name: http + containerPort: 8000 + protocol: TCP + + volumeMounts: + - name: settings-override + mountPath: /opt/app/myproject/settings.py + subPath: settings.py + + env: + - name: DOMAIN + value: "memelord-raiko.ee-lte-1.codemowers.io" + - name: DB_ENGINE + value: "postgres" + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: memelord-raiko-database + key: username + - name: POSTGRES_HOST + value: "memelord-raiko-database-rw" + - name: POSTGRES_PORT + value: "5432" + - name: POSTGRES_DB + value: "memelord-raiko" + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: memelord-raiko-database + key: password + - name: REDIS_HOST + value: "memelord-raiko-redis" + - name: REDIS_PORT + value: "6379" + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: memelord-raiko-redis + key: redis-password + - name: STORAGE_BACKEND + value: "s3" + - name: AWS_STORAGE_BUCKET_NAME + value: "memelord-raiko" + - name: AWS_S3_ENDPOINT_URL + value: "https://minio.ee-lte-1.codemowers.io/" + - name: AWS_S3_REGION_NAME + value: "ee-lte-1" + - name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: memelord-raiko-bucket + key: accessKey + - name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: memelord-raiko-bucket + key: secretKey + - name: OIDC_ENABLED + value: "True" + - name: OIDC_CREATE_USER + value: "True" + - name: OIDC_RP_CLIENT_ID + valueFrom: + secretKeyRef: + name: oidc-client-memelord-raiko-owner-secrets + key: OIDC_CLIENT_ID + - name: OIDC_RP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: oidc-client-memelord-raiko-owner-secrets + key: OIDC_CLIENT_SECRET + - name: OIDC_OP_AUTHORIZATION_ENDPOINT + value: "https://auth.ee-lte-1.codemowers.io/auth" + - name: OIDC_OP_TOKEN_ENDPOINT + value: "http://passmower.passmower.svc.cluster.local/token" + - name: OIDC_OP_USER_ENDPOINT + value: "http://passmower.passmower.svc.cluster.local/me" + - name: OIDC_OP_JWKS_ENDPOINT + value: "http://passmower.passmower.svc.cluster.local/jwks" + - name: OIDC_RP_SIGN_ALGO + value: "RS256" + - name: OIDC_AUTOLOGIN + value: "False" + - name: DEBUG + value: "True" + - name: SECURE_COOKIES + value: "True" + - name: ENABLE_PUBLIC_FEED + value: "True" + + volumes: + - name: settings-override + configMap: + name: memelord-settings + defaultMode: 420 diff --git a/deployment.yaml b/deployment.yaml new file mode 100644 index 0000000..9a2245d --- /dev/null +++ b/deployment.yaml @@ -0,0 +1,177 @@ +--- +apiVersion: secretgenerator.mittwald.de/v1alpha1 +kind: StringSecret +metadata: + name: memelord-raiko-redis +spec: + fields: + - fieldName: redis-password + length: "32" + encoding: hex +--- +apiVersion: dragonflydb.io/v1alpha1 +kind: Dragonfly +metadata: + name: memelord-raiko-redis +spec: + authentication: + passwordFromSecret: + name: memelord-raiko-redis + key: redis-password + replicas: 1 + resources: + requests: + cpu: 500m + memory: 500Mi + limits: + cpu: 600m + memory: 750Mi +--- +apiVersion: secretgenerator.mittwald.de/v1alpha1 +kind: StringSecret +metadata: + name: memelord-raiko-database + labels: + cnpg.io/reload: "true" +spec: + data: + username: memelord-raiko + fields: + - fieldName: password + length: "32" + encoding: hex +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: memelord-raiko-database +spec: + instances: 1 + imageName: ghcr.io/cloudnative-pg/postgresql:17 + storage: + size: 1Gi + storageClass: postgres + affinity: + podAntiAffinityType: required + nodeSelector: + codemowers.io/lvm-ubuntu-vg: enterprise-ssd + resources: + requests: + cpu: "100m" + memory: "1Gi" + limits: + cpu: "1" + memory: "4Gi" + postgresql: + parameters: + max_connections: "300" + shared_buffers: "512MB" + effective_cache_size: "2GB" + managed: + roles: + - name: memelord-raiko + ensure: present + login: true + passwordSecret: + name: memelord-raiko-database +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: memelord-raiko +spec: + name: memelord-raiko + owner: memelord-raiko + cluster: + name: memelord-raiko-database +--- +apiVersion: s3.onyxia.sh/v1alpha1 +kind: Policy +metadata: + name: memelord-raiko-policy +spec: + name: memelord-raiko-policy + s3InstanceRef: minio/default + policyContent: >- + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:*" + ], + "Resource": [ + "arn:aws:s3:::memelord-raiko", + "arn:aws:s3:::memelord-raiko/*" + ] + } + ] + } +--- +apiVersion: s3.onyxia.sh/v1alpha1 +kind: S3User +metadata: + name: memelord-raiko-bucket +spec: + accessKey: memelord-raiko-bucket + policies: + - memelord-raiko-policy + s3InstanceRef: minio/default +--- +apiVersion: s3.onyxia.sh/v1alpha1 +kind: Bucket +metadata: + name: memelord-raiko +spec: + name: memelord-raiko + s3InstanceRef: minio/default + quota: + default: 100000000 +--- +apiVersion: v1 +kind: Service +metadata: + name: memelord +spec: + type: ClusterIP + selector: + app: memelord + ports: + - name: http + port: 80 + targetPort: 8000 +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: memelord-raiko +spec: + secretName: memelord-raiko-tls + dnsNames: + - memelord-raiko.ee-lte-1.codemowers.io + issuerRef: + name: letsencrypt + kind: ClusterIssuer +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: memelord-raiko + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure +spec: + ingressClassName: traefik + rules: + - host: memelord-raiko.ee-lte-1.codemowers.io + http: + paths: + - pathType: Prefix + path: "/" + backend: + service: + name: memelord + port: + number: 80 + tls: + - secretName: memelord-raiko-tls