# Ограничения для кластера

## Ограничение по User Agent
В целях информационной безопасности и обеспечения корректной работы современных веб-приложений требуется ограничить доступ клиентов, использующих устаревшие версии браузеров. Такие браузеры не поддерживают актуальные криптографические протоколы и современные стандарты верстки, что делает их уязвимыми и несовместимыми с функционалом системы.

Ниже в разделах примеры настройки отклонения запросов по заголовку User-Agent на уровнях балансировщика HaProxy и Ingress-контроллера.

Для кластера ограничение можно настроить через HaProxy или Ingress.

### HaProxy

Добавить профиль конфигурации HaProxy для проброса в контейнер
`~/nscli/workspace/appkit/profile/haproxy/template/haproxy.cfg`:

<details>
  <summary>HaProxy.cfg</summary>

  ```bash
  global
        log stdout format short local0
        maxconn 40000
        stats socket {{work_dir}}/admin.sock mode 660 level admin
        stats timeout 30s
        user root
        group root
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private
    ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
    ssl-default-bind-options no-sslv3

    defaults
        log    global
        mode    http
        option    httplog
        option  forwardfor
        option    dontlognull
        timeout connect 120s
        timeout client  7d
        timeout server  7d
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 504 /etc/haproxy/errors/504.http
        errorfile 503 {{work_dir}}/503.no_servers.http
    {% if state == 'stopped' %}
    backend stopped
        errorfile 503 {{work_dir}}/503.stopped.http
    {% elif state == 'drained' %}
    backend drained
        errorfile 503 {{work_dir}}/503.drained.http
    {% endif %}
    frontend haproxynode{% if tls_crt_file  %}
                bind *:443 ssl crt {{tls_crt_file}} {% else %}
                bind *:8080 {% endif %}
                mode http {% if state == 'stopped' %}
                default_backend stopped {% elif state == 'drained' %}
                default_backend drained {% else %}
                http-request set-var(txn.path) path
                # Блокировка Microsoft Edge
                acl ua_edge hdr_sub(User-Agent) -i Edg/ #Поменять "Edg" на свой User Agent
                http-request deny status 403 if ua_edge
                acl has_x_forwarded_port req.hdr(X-Forwarded-Port) -m found
                http-request set-header X-Forwarded-Port %[req.hdr(X-Forwarded-Port)] if has_x_forwarded_port
                http-request set-header X-Forwarded-Port %[dst_port] unless has_x_forwarded_port
                http-request add-header X-Forwarded-Proto https if { ssl_fc }
                option forwardfor
                default_backend backendnodes
                use_backend debugger_selector if { path_beg /debugger/open }
                use_backend debugger if { path_beg /debugger }
                acl login_page var(txn.path) -i -m beg /login/login.html
                http-response return status 404 errorfile {{work_dir}}/404.shutdown.http if { status 404 } login_page
                
    backend backendnodes
                balance leastconn
                option httpchk
                http-check send meth HEAD uri /
                option persist
                cookie GS_BALANCER_SERVER_NAME insert indirect preserve
                
                {% for host in hosts %}{% if host.active_regular %}
                server {{host.name}} {{host.address}}:8080 check cookie {{host.name}}{% elif host.active_debug %}
                server {{host.name}} {{host.address}}:8080 disabled cookie {{host.name}}{% else %}
                server {{host.name}} {{host.address}}:8080 check cookie {{host.name}} disabled{% endif %}{% endfor %}
                {% if not hosts %}
                http-response return  status 503  errorfile {{work_dir}}/503.no_servers.http
                {% endif %}
                
    backend debugger
                option persist
                cookie GS_BALANCER_SERVER_NAME insert
                
                {% for host in hosts %}{% if host.active_debug %}
                server {{host.name}} {{host.address}}:9020 cookie {{host.name}} disabled{% endif %}{% endfor %}
                
    backend debugger_selector
                option persist
                cookie GS_BALANCER_SERVER_NAME insert
                http-request set-var(txn.query) query,url_dec(1)
                
                {% for host in hosts %}{% if host.active_debug %}
                http-request redirect  code 303 location %[str,concat("/",txn.query)] set-cookie GS_BALANCER_SERVER_NAME={{host.name}} if { path_beg -i /debugger/open/{{host.name}} }{% endif %}{% endfor %}
    {%- endif %}
                

    {% if stat_username  %} 
    listen stats
                bind :5000
                stats enable
                stats uri /
                stats hide-version
                stats auth {{stat_username}}:{{stat_password}}
    {% endif %}

    frontend prometheus
                bind *:8405
                mode http
                http-request use-service prometheus-exporter if { path /metrics }
                no log

    listen health
                bind *:6000
                mode http
                monitor-uri  /is_alive


  ```
</details>

Применить профиль конфигурации:
```bash
./appkit.sh prepare_profile --appkit-dir workspace/appkit 
./appkit.sh push --namespace gs-ctk --source ./workspace/appkit --destination appkit/v1 
./appkit.sh switch_local --config-path ./config.yaml --resgroup gs-cluster-1 --local-appkit ./workspace/appkit --remote-appkit appkit/v1
./appkit.sh start --config-path ./config.yaml --resgroup gs-cluster-1
kubectl apply -f ./config.yaml
```




### Ingress-Nginx

В ingress-nginx должно быть включено:

`allow-snippet-annotations: "true"`

`EDITOR=nano kubectl edit ingress -n gs-ctk ctk-restapi` - отредактировать сщуествующую конфигурацию

или

Применить из файла `ingress.yaml`:

```bash 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: if ($http_user_agent ~* "Firefox")
      { return 403; }
    nginx.ingress.kubernetes.io/server-snippet: if ($http_user_agent ~* "Firefox")
      { return 403; }
  creationTimestamp: "2026-06-05T14:49:04Z"
  name: ctk-restapi
  namespace: gs-ctk
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - backend:
          service:
            name: gs-cluster-1-haproxy-external
            port:
              number: 8080
        path: /
        pathType: Prefix
```

Применить конфигурацию `kubectl apply -f ingress.yaml`

### Ingress-HaProxy


Создайте или отредактируйте ConfigMap `haproxy-ingress` в namespace `haproxy-ingress`:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: haproxy-ingress
  namespace: haproxy-ingress
  annotations:
    # Защита от перезаписи при helm upgrade
    helm.sh/resource-policy: keep
data:
  # Правила для фронтенда (HTTP/HTTPS)
  config-frontend: |-
    # Блокировка браузера Opera (возвращает 403)
    http-request deny if { hdr_sub(user-agent) -i Opera }
    # Перезапись заголовка Location: убрать :8080 из редиректов
    http-response set-header Location "%[res.hdr(Location),regsub(:8080,,i)]"
  healthz-port: "10253"
  stats-port: "1936"
```

Применить через kubectl:

```bash
kubectl apply -f haproxy-ingress-configmap.yaml
```
