# Проксирование внешнего хоста через Traefik (Ingress-контроллер) У меня в домашней сети есть хост с web-сервисом (audiobookshelf), а стандартные web-порты (80 для HTTP и 443 для HTTPS) на домашнем роутере перенаправлены в кластер k3S (через keepalived). Таким образом, если прокинуть http-трафик этого хоста через Traefik, то можно будет получить доступ к этому сервису через доменное имя и SSL-сертификат от Let’s Encrypt. Для удобства я поместил все манифесты в один файл (вы можете оформить из и как отдельные файлы). Так как хотелось бы описать как делаются универсальные манифесты, которые можно использовать для проксирования любого сервиса, то я заменил в нем конкретные значения на "заглушки. Можно взять этот манифест и просто заменить в нем значения на свои: * `` -- IP-адрес хоста, где работает сервис, который надо проксировать. * `` -- порт, с которого отвечает сервис. * `` -- доменное имя, на которое будет проксировать сервис. * `` -- пространство имен кластера, в котором будет создан сервис, маршруты, секреты и все необходимое для проксирования. Пространство имен -- это логическая группа ресурсов в кластере Kubernetes, которая позволяет организовать и изолировать ресурсы. * `` -- имя сервиса, который будет проксироваться. Это имя, для простоты, будем использоваться * и в маршрутах, и сертификатах, и в секрете... ## Пространство имен Чтобы все было аккуратно и сервисы и поды не путались, создадим пространство имен для проксирования конкретного хоста. Мо хост относится к , поэтому назову пространство имен `` Например, ``. ```bash sudo ubectl create namespace ``` Проверяем, что пространство создано: ```bash sudo kubectl get namespace ``` Увидим, что пространство создано и активно: ```text NAME STATUS AGE Active 54s ``` ## Конфигурация всего Для удобства я объединил манифесты в один файл (но можно и по отдельности). Создаем единый манифест: ```bash sudo nano ~/k3s//.yaml ``` Он состоит из следующих частей: * `Endpoints` -- указывает на конечную точку, в которую будет проксироваться трафик. В данном случае это IP-адрес и порт, на который будет проксироваться запрос. * `Service` -- создает сервис, который будет использоваться для проксирования запросов к `Endpoints`. * `Certificate` -- создает сертификат для домена ``, который будет использоваться для шифрования трафика. Сертификат запрашивается через `cert-manager`, который автоматически обновляет его по мере необходимости. * `Middleware` -- создает промежуточное ПО, которое будет использоваться для обработки запросов. В данном случае это редирект с HTTP на HTTPS и исключение редиректа для ACME challenge (механизма внешней проверки владения доменом со стороны Let’s Encrypt). * `IngressRoute` -- создает маршрут, который будет использоваться для проксирования запросов к сервису. В данном случае это маршруты для HTTP и HTTPS, которые будут обрабатывать запросы на домен ``. Также создается маршрут для ACME challenge, который позволяет cert-manager пройти проверку через порт 80. Вставляем в манифест следующее содержимое (не забудьте заменить ``, ``, ``, `` и `` на свои значения): ```yaml # Endpoints для внешнего хоста # Задаёт IP и порт внешнего сервера, так как внешний хост для k3s apiVersion: v1 kind: Endpoints metadata: name: namespace: # Namespace для subsets: # Прямо в корне, без spec - addresses: - ip: # IP Synology, где работает ports: - port: # Порт Synology (HTTP) protocol: TCP --- # Service для маршрутизации трафика от Traefik к внешнему хосту # Связывает IngressRoute с Endpoints apiVersion: v1 kind: Service metadata: name: namespace: spec: ports: - port: # Порт сервиса, на который Traefik отправляет трафик targetPort: # Порт на Synology protocol: TCP --- # Middleware для редиректа HTTP → HTTPS # Применяется к HTTP-запросам для перенаправления на HTTPS apiVersion: traefik.io/v1alpha1 # версия Traefik v34.2.1+up34.2.0 (Traefik v3.3.6) # apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: https-redirect namespace: spec: redirectScheme: scheme: https # Перенаправлять на HTTPS permanent: true # Код 301 (постоянный редирект) --- # Middleware для исключения редиректа на ACME challenge # Позволяет Let’s Encrypt проверять /.well-known/acme-challenge без HTTPS apiVersion: traefik.io/v1alpha1 # версия Traefik v34.2.1+up34.2.0 (Traefik v3.3.6) # apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: no-https-redirect namespace: spec: stripPrefix: prefixes: - /.well-known/acme-challenge # Убрать префикс для ACME --- # IngressRoute для HTTP (порт 80) с редиректом на HTTPS # Обрабатывает HTTP-запросы и перенаправляет их apiVersion: traefik.io/v1alpha1 # версия Traefik v34.2.1+up34.2.0 (Traefik v3.3.6) # apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: -http namespace: spec: entryPoints: - web # Порт 80 (стандартный HTTP) routes: - match: Host("") # Запросы к kind: Rule services: - name: # Сервис port: # Порт сервиса middlewares: - name: https-redirect # Редирект на HTTPS --- # IngressRoute для HTTPS (порт 443) # Обрабатывает HTTPS-запросы с TLS apiVersion: traefik.io/v1alpha1 # версия Traefik v34.2.1+up34.2.0 (Traefik v3.3.6) # apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: -https namespace: spec: entryPoints: - websecure # Порт 443 (HTTPS) routes: - match: Host("") kind: Rule services: - name: port: tls: secretName: -tls # Сертификат от cert-manager --- # IngressRoute для HTTP-01 challenge (Let’s Encrypt) # Позволяет cert-manager пройти проверку через порт 80 apiVersion: traefik.io/v1alpha1 # версия Traefik v34.2.1+up34.2.0 (Traefik v3.3.6) # apiVersion: traefik.containo.us/v1alpha1 # старая версия Traefik kind: IngressRoute metadata: name: -acme namespace: spec: entryPoints: - web # Порт 80 routes: - match: Host("") && PathPrefix("/.well-known/acme-challenge") kind: Rule services: - name: port: middlewares: - name: no-https-redirect # Не редиректить ACME --- # Certificate для TLS-сертификата от Let’s Encrypt # Запрашивает сертификат для через cert-manager # ВАЖНО: cert-manager должен быть установлен в кластере # ВАЖНО: если манифесты принимаются (apply) последовательно, то манифест с сертификатом должен быть последним для # избежания исчерпания лимитов Let’s Encrypt (пять запросов в неделю) apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: -tls namespace: spec: secretName: -tls # Имя секрета для сертификата dnsNames: - # Домен для сертификата issuerRef: name: letsencrypt-prod # ClusterIssuer для Let’s Encrypt kind: ClusterIssuer ``` **ВАЖНО**: В манифесте используется letsencrypt-prod для получения сертификата от Let’s Encrypt. Это нестандартный ClusterIssuer cert-manager, создание которого описано в [документации](https://cert-manager.io/docs/usage/ingress/#tls-termination) и [отдельной инструкции](k3s-custom-container-deployment.md#создание-clusterissuer) (возможно, вам нужно будет создать его отдельно). Если вы используете другой ClusterIssuer, то замените letsencrypt-prod на имя вашего ClusterIssuer в секции `issuerRef` в манифесте. ## Применяем манифест ```bash sudo kubectl apply -f ~/k3s//.yaml ``` Проверяем, что все ресурсы создались: ```bash sudo kubectl get all -n sudo kubectl get ingressroute -n sudo kubectl get middleware -n sudo kubectl get service -n sudo kubectl get endpoints -n ``` Увидим что-то вроде: ```text NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/ ClusterIP 10.43.152.59 /TCP 3h ``` Для IngressRoute: ```text NAME AGE 3h -acme 1h -http 1h -https 1h ``` Для Middleware: ```text NAME AGE https-redirect 1h no-https-redirect 1h ``` Для Service: ```text NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ClusterIP 10.43.152.59 /TCP 3h ``` Для Endpoints: ```text NAME ENDPOINTS AGE : 3h ``` Проверяем, что сертификат создан: ```bash sudo kubectl describe certificate -n -tls sudo kubectl get secret -n -tls ```