Введение
Пространства имён помогают организовать ресурсы, но сами по себе не блокируют сетевой трафик. NetworkPolicy определяет, какие выбранные Pod могут устанавливать или принимать соединения, если сетевой плагин кластера поддерживает применение этих правил. В подготовленной среде используется Calico, который это обеспечивает.
Сначала вы убедитесь, что трафик изначально разрешён, затем примените запрет по умолчанию и оставите доступ только клиенту с нужной меткой из доверенного пространства имён.
В лабораторной работе используется безопасный порядок настройки политик: сначала фиксируется рабочее состояние, затем трафик блокируется, после чего добавляется минимально необходимое исключение и проверяются как разрешённый, так и запрещённый сценарии. Если проверить только разрешённого клиента, слишком широкая политика может остаться незамеченной.
Разверните сервер и клиентов
На этом шаге создайте отдельные пространства имён для приложения и клиентов, NGINX-сервер и два постоянно работающих клиентских Pod.
Команды намеренно используют привычный императивный синтаксис, чтобы эксперимент с сетью не затерялся среди не связанных с ним YAML-файлов. Один клиент получает метку role=approved, другой — role=untrusted. Пространство имён клиентов получает метку access=approved. Позже селекторы политик будут использовать оба уровня меток.
cd /home/labex/project/network-policy
kubectl create namespace practice-apps
kubectl create namespace practice-clients
kubectl label namespace practice-clients access=approved
Создайте сервер и опубликуйте его. Команда kubectl expose создаёт сервис ClusterIP, селектор которого соответствует метке Pod app=protected-web, автоматически добавленной к Pod развёртывания:
kubectl create deployment protected-web -n practice-apps --image=nginx:1.27-alpine --replicas=1
kubectl expose deployment protected-web -n practice-apps --port=80 --target-port=80
Создайте два диагностических Pod-клиента. Их циклы ожидания позволяют многократно выполнять проверки с помощью kubectl exec:
kubectl run напрямую создаёт каждый отдельный Pod. Параметр --labels добавляет идентификатор, по которому политика будет выбирать Pod, --restart=Never запрещает kubectl создавать контроллер рабочей нагрузки, а -- отделяет параметры kubectl от команды sh -c, выполняемой внутри контейнера. В команде ожидания параметр --all означает, что готовыми должны стать все Pod в practice-clients.
kubectl run approved-client -n practice-clients --image=busybox:1.36 --image-pull-policy=IfNotPresent --labels=role=approved --restart=Never -- sh -c 'while true; do sleep 30; done'
kubectl run untrusted-client -n practice-clients --image=busybox:1.36 --image-pull-policy=IfNotPresent --labels=role=untrusted --restart=Never -- sh -c 'while true; do sleep 30; done'
kubectl wait --for=condition=Ready pod -n practice-clients --all --timeout=120s
kubectl rollout status deployment/protected-web -n practice-apps --timeout=60s
Одного лишь разделения по пространствам имён по-прежнему достаточно, чтобы оба клиента могли обратиться к сервису.
Команды проверки готовности должны завершиться сообщениями о готовности обоих клиентских Pod и развёртывания сервера:
pod/approved-client condition met
pod/untrusted-client condition met
deployment "protected-web" successfully rolled out
На этом этапе метки лишь описывают клиентов, но не ограничивают их доступ. Метка становится правилом управления трафиком только после того, как селектор NetworkPolicy начинает её использовать.
Убедитесь, что трафик изначально разрешён
На этом шаге обратитесь к сервису из другого пространства имён от имени обоих клиентов. Полное доменное имя включает имя сервиса и пространства имён.
В том же пространстве имён было бы достаточно имени protected-web. Из practice-clients имя protected-web.practice-apps сообщает кластерной DNS и имя сервиса, и его пространство имён. Проверьте обоих клиентов до применения политики, чтобы последующие ошибки можно было связать именно с политикой, а не с DNS или настройкой сервиса.
Как и в лабораторной работе с проверкой работоспособности, wget -qO- выводит тело ответа без индикатора выполнения. Параметр --timeout=3 не позволяет заблокированному запросу зависнуть надолго, а конвейер с grep выводит только узнаваемый текст NGINX.
cd /home/labex/project/network-policy
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl exec untrusted-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl get networkpolicies -n practice-apps
Если ни одна NetworkPolicy не выбирает Pod, входящий трафик разрешён. Это опровергает распространённое заблуждение, будто пространства имён сами по себе являются границами сетевой безопасности.
Обе команды должны найти строку Welcome to nginx, а kubectl get networkpolicies должен сообщить об отсутствии ресурсов. Это исходное заведомо рабочее состояние, которое вы намеренно измените.
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
No resources found in practice-apps namespace.
Повторяющийся текст страницы доказывает, что оба клиента могут обращаться к одному и тому же сервису до начала изоляции.
Примените запрет входящего трафика по умолчанию
На этом шаге выберите все Pod в practice-apps и не разрешайте ни одного источника входящего трафика.
NetworkPolicy ограничивает трафик только для Pod, выбранных этой политикой, и только в направлении, указанном в policyTypes. Здесь podSelector: {} выбирает все Pod в practice-apps, а отсутствие разрешающих правил ingress создаёт запрет входящего трафика по умолчанию.
cd /home/labex/project/network-policy
cat <<'EOF' > default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: default-deny-ingress, namespace: practice-apps}
spec:
podSelector: {}
policyTypes: [Ingress]
EOF
kubectl apply -f default-deny.yaml
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps || echo 'blocked as expected'
Пустой podSelector выбирает каждый Pod в пространстве имён политики. Ожидаемое истечение времени ожидания доказывает, что входящий трафик теперь запрещён, а не то, что сервис исчез.
Конструкция || echo ... обрабатывает ожидаемый ненулевой код завершения wget, позволяя терминалу продолжить работу. В этом контролируемом эксперименте ошибка сама является свидетельством результата. Серверный Pod остаётся готовым — изменился только допуск сетевых соединений.
networkpolicy.networking.k8s.io/default-deny-ingress created
wget: download timed out
command terminated with exit code 1
blocked as expected
Истечение времени ожидания ожидаемо: заблокированный пакет не получает ответа от приложения. Последняя строка подтверждает, что оболочка намеренно преобразовала ожидаемую ошибку в понятный результат.
Разрешите доступ одному доверенному клиенту
На этом шаге добавьте разрешающую политику, которая требует одновременно наличия нужной метки у пространства имён и у Pod-клиента.
Сетевые политики работают аддитивно. Политика с запретом по умолчанию продолжает действовать, а вторая политика добавляет один разрешённый источник. Выбранный серверный Pod может принимать TCP-соединения на порту 80 только тогда, когда совпадают и пространство имён источника, и метка исходного Pod.
cd /home/labex/project/network-policy
cat <<'EOF' > allow-approved.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: allow-approved-client, namespace: practice-apps}
spec:
podSelector: {matchLabels: {app: protected-web}}
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector: {matchLabels: {access: approved}}
podSelector: {matchLabels: {role: approved}}
ports:
- {protocol: TCP, port: 80}
EOF
kubectl apply -f allow-approved.yaml
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl exec untrusted-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps || echo 'untrusted client blocked'
Поскольку оба селектора находятся в одной записи from, должны совпасть оба условия. В отдельных записях они означали бы логическое ИЛИ.
Запрос доверенного клиента снова должен завершиться успешно, а запрос недоверенного клиента — по-прежнему завершиться по тайм-ауту. Такой парный результат доказывает, что политика разрешает доступ выборочно, а не открывает весь входящий трафик.
networkpolicy.networking.k8s.io/allow-approved-client created
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
wget: download timed out
command terminated with exit code 1
untrusted client blocked
Интерпретируйте результат как пару: содержимое страницы для approved-client, затем тайм-аут для untrusted-client. Один успешный запрос не доказал бы, что правило достаточно строго ограничено.
Итоги
Вы убедились, что пространства имён по умолчанию не изолируют трафик, применили запрет входящего трафика и разрешили доступ одному клиенту по совокупности критериев пространства имён, Pod, протокола и порта.


