Мережеві політики

Мережеві політики

Якщо ви хочете контролювати потік трафіку на рівні IP-адреси чи порту (рівень 3 або 4 моделі OSI), NetworkPolicies дозволяють визначати правила потоку трафіку всередині вашого кластера, а також між Podʼами та зовнішнім світом. Ваш кластер повинен використовувати мережевий втулок, який підтримує застосування NetworkPolicy.

Якщо ви хочете контролювати потік трафіку на рівні IP-адреси чи порту для протоколів TCP, UDP та SCTP, вам варто розглянути використання NetworkPolicies Kubernetes для окремих застосунків у вашому кластері. NetworkPolicies — це конструкція, орієнтована на застосунки, яка дозволяє визначити, як Pod може взаємодіяти через мережу з різними мережевими «сутностями» (ми вживаємо тут слово «сутність», щоб не перевантажувати поширеніші терміни, як-от «точки доступу» та «Services», які мають у Kubernetes особливі значення). NetworkPolicies застосовуються до зʼєднання, на одному або обох кінцях якого є Pod, і не стосуються інших зʼєднань.

Сутності, з якими може взаємодіяти Pod, визначаються за допомогою комбінації таких трьох ідентифікаторів:

  1. Інші дозволені Podʼи (виняток: Pod не може заблокувати доступ до самого себе)
  2. Дозволені простори імен
  3. IP-блоки (виняток: трафік до вузла і від вузла, де працює Pod, завжди дозволений, незалежно від IP-адреси Podʼа чи вузла)

Під час визначення NetworkPolicy на основі Podʼів або просторів імен ви використовуєте селектор, щоб вказати, який трафік дозволений до Podʼів і від Podʼів, що відповідають цьому селектору.

Натомість під час створення NetworkPolicies на основі IP ми визначаємо політики на основі IP-блоків (діапазонів CIDR).

Передумови

Мережеві політики реалізуються мережевим втулком. Щоб використовувати мережеві політики, вам потрібне мережеве рішення, яке підтримує NetworkPolicy. Створення ресурсу NetworkPolicy без контролера, який його реалізує, не матиме ефекту.

Два види ізоляції Podʼів

Існує два види ізоляції для Podʼа: ізоляція для вихідного трафіку (egress) та ізоляція для вхідного трафіку (ingress). Вони стосуються того, які зʼєднання можуть бути встановлені. «Ізоляція» тут не є абсолютною, а означає «застосовуються певні обмеження». Альтернатива, «неізольований для $direction», означає, що в зазначеному напрямку обмежень немає. Обидва види ізоляції (або її відсутності) оголошуються незалежно, і обидва мають значення для зʼєднання від одного Podʼа до іншого.

Стандартно Pod не є ізольованим для вихідного трафіку: усі вихідні зʼєднання дозволені. Pod є ізольованим для вихідного трафіку, якщо існує будь-яка NetworkPolicy, яка одночасно вибирає цей Pod і має "Egress" у своєму policyTypes; ми кажемо, що така політика застосовується до Podʼа для вихідного трафіку. Коли Pod ізольований для вихідного трафіку, єдині дозволені зʼєднання з Podʼа — це ті, що дозволені списком egress якоїсь NetworkPolicy, яка застосовується до Podʼа для вихідного трафіку. Трафік відповідей для цих дозволених зʼєднань також буде неявно дозволений. Дія цих списків egress обʼєднується адитивно.

Стандартно Pod не є ізольованим для вхідного трафіку: усі вхідні зʼєднання дозволені. Pod є ізольованим для вхідного трафіку, якщо існує будь-яка NetworkPolicy, яка одночасно вибирає цей Pod і має "Ingress" у своєму policyTypes; ми кажемо, що така політика застосовується до Podʼа для вхідного трафіку. Коли Pod ізольований для вхідного трафіку, єдині дозволені зʼєднання до Podʼа — це зʼєднання з вузла Podʼа та ті, що дозволені списком ingress якоїсь NetworkPolicy, яка застосовується до Podʼа для вхідного трафіку. Трафік відповідей для цих дозволених зʼєднань також буде неявно дозволений. Дія цих списків ingress обʼєднується адитивно.

Мережеві політики не конфліктують, вони адитивні. Якщо одна чи кілька політик застосовуються до певного Podʼа в певному напрямку, то дозволені в цьому напрямку зʼєднання цього Podʼа — це обʼєднання того, що дозволяють застосовні політики. Тому порядок обчислення не впливає на результат політики.

Щоб зʼєднання від Podʼа-джерела до Podʼа-призначення було дозволене, його мають дозволяти і політика вихідного трафіку на Podʼі-джерелі, і політика вхідного трафіку на Podʼі-призначенні. Якщо хоча б одна сторона не дозволяє зʼєднання, воно не відбудеться.

Ресурс NetworkPolicy

Повне визначення ресурсу дивіться в довіднику NetworkPolicy.

Приклад NetworkPolicy може виглядати так:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Примітка:

Надсилання цього маніфесту (POST) до API-сервера вашого кластера не матиме жодного ефекту, якщо обране вами мережеве рішення не підтримує мережеві політики.

Обовʼязкові поля: Як і всім іншим конфігураціям Kubernetes, NetworkPolicy потрібні поля apiVersion, kind та metadata. Загальну інформацію про роботу з файлами конфігурації дивіться в Налаштування Podʼів для використання ConfigMap та Управління обʼєктами.

spec: Специфікація NetworkPolicy містить усю інформацію, необхідну для визначення конкретної мережевої політики в заданому просторі імен.

podSelector: Кожна NetworkPolicy містить podSelector, який вибирає групу Podʼів, до яких застосовується політика. Політика з прикладу вибирає Podʼи з міткою "role=db". Порожній podSelector вибирає всі Podʼи в просторі імен.

policyTypes: Кожна NetworkPolicy містить список policyTypes, який може включати Ingress, Egress або обидва значення. Поле policyTypes вказує, чи застосовується ця політика до вхідного трафіку до вибраних Podʼів, до вихідного трафіку від вибраних Podʼів чи до обох. Якщо в NetworkPolicy не вказано жодного policyTypes, то стандартно завжди буде встановлено Ingress, а Egress буде встановлено, якщо NetworkPolicy має хоча б одне правило вихідного трафіку.

ingress: Кожна NetworkPolicy може містити список дозволених правил ingress. Кожне правило дозволяє трафік, який відповідає обом секціям: from і ports. Політика з прикладу містить одне правило, яке відповідає трафіку на одному порту від одного з трьох джерел: перше вказано через ipBlock, друге через namespaceSelector, а третє через podSelector.

egress: Кожна NetworkPolicy може містити список дозволених правил egress. Кожне правило дозволяє трафік, який відповідає обом секціям: to і ports. Політика з прикладу містить одне правило, яке відповідає трафіку на одному порту до будь-якого призначення в 10.0.0.0/24.

Отже, NetworkPolicy з прикладу:

  1. ізолює Podʼи role=db у просторі імен default як для вхідного, так і для вихідного трафіку (якщо вони ще не були ізольовані)

  2. (правила Ingress) дозволяє зʼєднання до всіх Podʼів у просторі імен default з міткою role=db на TCP-порт 6379 від:

    • будь-якого Podʼа в просторі імен default з міткою role=frontend
    • будь-якого Podʼа в просторі імен з міткою project=myproject
    • IP-адрес у діапазонах 172.17.0.0–172.17.0.255 та 172.17.2.0–172.17.255.255 (тобто всіх адрес 172.17.0.0/16, крім 172.17.1.0/24)
  3. (правила Egress) дозволяє зʼєднання від будь-якого Podʼа в просторі імен default з міткою role=db до CIDR 10.0.0.0/24 на TCP-порт 5978

Додаткові приклади дивіться в покроковому посібнику Оголошення мережевої політики.

Поведінка селекторів to та from

Існує чотири види селекторів, які можна вказати в секції from правила ingress або в секції to правила egress:

podSelector: Вибирає певні Podʼи в тому самому просторі імен, що й NetworkPolicy, які мають бути дозволені як джерела вхідного трафіку або призначення вихідного трафіку.

namespaceSelector: Вибирає певні простори імен, усі Podʼи яких мають бути дозволені як джерела вхідного трафіку або призначення вихідного трафіку.

namespaceSelector та podSelector: Один запис to/from, який вказує і namespaceSelector, і podSelector, вибирає певні Podʼи в певних просторах імен. Будьте уважні й використовуйте правильний синтаксис YAML. Наприклад:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

Ця політика містить один елемент from, який дозволяє зʼєднання від Podʼів з міткою role=client у просторах імен з міткою user=alice. Але наведена далі політика відрізняється:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

Вона містить два елементи в масиві from і дозволяє зʼєднання від Podʼів у локальному просторі імен з міткою role=client або від будь-якого Podʼа в будь-якому просторі імен з міткою user=alice.

Якщо є сумніви, скористайтеся kubectl describe, щоб побачити, як Kubernetes інтерпретував політику.

ipBlock: Вибирає певні діапазони IP CIDR, дозволені як джерела вхідного трафіку або призначення вихідного трафіку. Це мають бути IP-адреси поза кластером, оскільки IP-адреси Podʼів є ефемерними та непередбачуваними.

Механізми вхідного та вихідного трафіку кластера часто вимагають переписування IP-адреси джерела або призначення пакетів. У таких випадках не визначено, чи відбувається це до обробки NetworkPolicy, чи після неї, і поведінка може відрізнятися для різних комбінацій мережевого втулка, хмарного провайдера, реалізації Service тощо.

Для вхідного трафіку це означає, що в одних випадках ви можете фільтрувати вхідні пакети за фактичною початковою IP-адресою джерела, а в інших «IP-адреса джерела», за якою працює NetworkPolicy, може бути IP-адресою LoadBalancer, вузла Podʼа тощо.

Для вихідного трафіку це означає, що на зʼєднання від Podʼів до IP-адрес Service, які переписуються на зовнішні для кластера IP-адреси, політики на основі ipBlock можуть поширюватися, а можуть і не поширюватися.

Стандартні політики

Стандартно, якщо в просторі імен немає жодної політики, весь вхідний і вихідний трафік до Podʼів і від Podʼів у цьому просторі імен дозволений. Наведені нижче приклади дозволяють змінити стандартну поведінку в цьому просторі імен.

Стандартна заборона всього вхідного трафіку

Ви можете створити «стандартну» політику ізоляції вхідного трафіку для простору імен, створивши NetworkPolicy, яка вибирає всі Podʼи, але не дозволяє жодного вхідного трафіку до цих Podʼів.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

Це гарантує, що навіть Podʼи, не вибрані жодною іншою NetworkPolicy, залишатимуться ізольованими для вхідного трафіку. Ця політика не впливає на ізоляцію вихідного трафіку будь-якого Podʼа.

Дозвіл на весь вхідний трафік

Якщо ви хочете дозволити всі вхідні зʼєднання до всіх Podʼів у просторі імен, ви можете створити політику, яка явно це дозволяє.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

За наявності цієї політики жодна додаткова політика чи політики не можуть призвести до відмови у вхідному зʼєднанні до цих Podʼів. Ця політика не впливає на ізоляцію вихідного трафіку будь-якого Podʼа.

Стандартна заборона всього вихідного трафіку

Ви можете створити «стандартну» політику ізоляції вихідного трафіку для простору імен, створивши NetworkPolicy, яка вибирає всі Podʼи, але не дозволяє жодного вихідного трафіку від цих Podʼів.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

Це гарантує, що навіть Podʼам, не вибраним жодною іншою NetworkPolicy, не буде дозволено вихідний трафік. Ця політика не змінює поведінку ізоляції вхідного трафіку будь-якого Podʼа.

Увага:

Стандартна політика заборони всього вихідного трафіку також блокує DNS-трафік. Якщо вашим робочим навантаженням потрібне розвʼязання DNS-імен, ви повинні додати окрему NetworkPolicy, яка дозволяє вихідний трафік до DNS-сервісу вашого кластера.

Дозвіл на весь вихідний трафік

Якщо ви хочете дозволити всі зʼєднання від усіх Podʼів у просторі імен, ви можете створити політику, яка явно дозволяє всі вихідні зʼєднання від Podʼів у цьому просторі імен.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

За наявності цієї політики жодна додаткова політика чи політики не можуть призвести до відмови у вихідному зʼєднанні від цих Podʼів. Ця політика не впливає на ізоляцію вхідного трафіку будь-якого Podʼа.

Стандартна заборона всього вхідного та всього вихідного трафіку

Ви можете створити «стандартну» політику для простору імен, яка забороняє весь вхідний І вихідний трафік, створивши в цьому просторі імен наведену нижче NetworkPolicy.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Це гарантує, що навіть Podʼам, не вибраним жодною іншою NetworkPolicy, не буде дозволено вхідний або вихідний трафік.

Фільтрація мережевого трафіку

NetworkPolicy визначена для зʼєднань рівня 4 (TCP, UDP та, за потреби, SCTP). Для всіх інших протоколів поведінка може відрізнятися залежно від мережевого втулка.

Примітка:

Ви повинні використовувати втулок CNI, який підтримує NetworkPolicies для протоколу SCTP.

Коли визначено мережеву політику deny all, гарантовано забороняються лише зʼєднання TCP, UDP та SCTP. Для інших протоколів, таких як ARP чи ICMP, поведінка не визначена. Те саме стосується дозвільних правил: коли певний Pod дозволений як джерело вхідного трафіку або призначення вихідного трафіку, не визначено, що відбувається (наприклад) з пакетами ICMP. Такі протоколи, як ICMP, можуть бути дозволені одними мережевими втулками та заборонені іншими.

Націлювання на діапазон портів

СТАН ФУНКЦІОНАЛУ: GA since Kubernetes v1.25

Під час написання NetworkPolicy ви можете націлитися на діапазон портів, а не на один порт.

Це можна зробити за допомогою поля endPort, як показано в наведеному нижче прикладі:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 32000
          endPort: 32768

Наведене вище правило дозволяє будь-якому Podʼу з міткою role=db у просторі імен default взаємодіяти з будь-якою IP-адресою в діапазоні 10.0.0.0/24 через TCP за умови, що цільовий порт перебуває в діапазоні від 32000 до 32768.

Під час використання цього поля застосовуються такі обмеження:

  • Поле endPort має бути рівним полю port або більшим за нього.
  • endPort можна визначити, лише якщо також визначено port.
  • Обидва порти мають бути числовими.

Примітка:

Ваш кластер повинен використовувати втулок CNI, який підтримує поле endPort у специфікаціях NetworkPolicy. Якщо ваш мережевий втулок не підтримує поле endPort, а ви вкажете NetworkPolicy із цим полем, політика буде застосована лише для одного поля port.

Націлювання на кілька просторів імен за міткою

У цьому сценарії ваша NetworkPolicy типу Egress націлена на кілька просторів імен за назвами їхніх міток. Щоб це працювало, потрібно додати мітки до цільових просторів імен. Наприклад:

kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend

Додайте мітки в namespaceSelector у документі NetworkPolicy. Наприклад:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-namespaces
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchExpressions:
        - key: namespace
          operator: In
          values: ["frontend", "backend"]

Примітка:

Неможливо безпосередньо вказати назви просторів імен у NetworkPolicy. Ви повинні використовувати namespaceSelector із matchLabels або matchExpressions, щоб вибрати простори імен за їхніми мітками.

Націлювання на простір імен за його назвою

Панель управління Kubernetes встановлює незмінну мітку kubernetes.io/metadata.name на всі простори імен, значенням мітки є назва простору імен.

Хоча NetworkPolicy не може націлитися на простір імен за його назвою через якесь поле обʼєкта, ви можете використати стандартизовану мітку, щоб націлитися на конкретний простір імен.

Життєвий цикл Podʼа

Примітка:

Наведене нижче стосується кластерів із сумісним мережевим втулком та сумісною реалізацією NetworkPolicy.

Коли створюється новий обʼєкт NetworkPolicy, мережевому втулку може знадобитися певний час, щоб обробити цей новий обʼєкт. Якщо Pod, на який впливає NetworkPolicy, створено до того, як мережевий втулок завершив обробку NetworkPolicy, цей Pod може бути запущений незахищеним, а правила ізоляції будуть застосовані, коли обробку NetworkPolicy буде завершено.

Щойно NetworkPolicy оброблено мережевим втулком,

  1. Усі новостворені Podʼи, на які впливає певна NetworkPolicy, будуть ізольовані до їхнього запуску. Реалізації NetworkPolicy повинні гарантувати, що фільтрація діє протягом усього життєвого циклу Podʼа, починаючи з першої миті запуску будь-якого контейнера в цьому Podʼі. Оскільки NetworkPolicies застосовуються на рівні Podʼа, вони однаково застосовуються до init-контейнерів, sidecar-контейнерів і звичайних контейнерів.

  2. Дозвільні правила будуть зрештою застосовані після правил ізоляції (або можуть бути застосовані одночасно з ними). У найгіршому випадку новостворений Pod може взагалі не мати мережевого звʼязку під час першого запуску, якщо правила ізоляції вже застосовані, а дозвільні правила ще ні.

Кожна створена NetworkPolicy рано чи пізно буде оброблена мережевим втулком, але через API Kubernetes неможливо дізнатися, коли саме це станеться.

Тому Podʼи мають бути стійкими до запуску з іншим мережевим звʼязком, ніж очікувалося. Якщо вам потрібно переконатися, що Pod може досягти певних призначень до запуску, ви можете використати init-контейнер, щоб дочекатися доступності цих призначень, перш ніж kubelet запустить контейнери застосунків.

Кожна NetworkPolicy рано чи пізно буде застосована до всіх вибраних Podʼів. Оскільки мережевий втулок може реалізовувати NetworkPolicy розподілено, Podʼи можуть бачити дещо неузгоджене подання мережевих політик під час першого створення Podʼа або коли змінюються Podʼи чи політики. Наприклад, новостворений Pod, який має мати доступ і до Podʼа A на вузлі 1, і до Podʼа B на вузлі 2, може виявити, що Pod A доступний йому одразу, а Pod B — лише через кілька секунд.

NetworkPolicy та Podʼи з hostNetwork

Поведінка NetworkPolicy для Podʼів із hostNetwork не визначена, але вона має обмежуватися двома можливостями:

  • Мережевий втулок може відрізняти трафік Podʼів із hostNetwork від усього іншого трафіку (зокрема, відрізняти трафік різних Podʼів із hostNetwork на тому самому вузлі) і застосовує NetworkPolicy до Podʼів із hostNetwork так само, як і до Podʼів у мережі Podʼів.
  • Мережевий втулок не може належним чином відрізнити трафік Podʼів із hostNetwork, тому він ігнорує такі Podʼи під час зіставлення podSelector та namespaceSelector. Трафік до Podʼів і від Podʼів із hostNetwork обробляється так само, як і весь інший трафік до IP-адреси вузла та від неї. (Це найпоширеніша реалізація.)

Це стосується випадків, коли

  1. Pod із hostNetwork вибирається за допомогою spec.podSelector.

      ...
      spec:
        podSelector:
          matchLabels:
            role: client
      ...
    
  2. Pod із hostNetwork вибирається за допомогою podSelector чи namespaceSelector у правилі ingress чи egress.

      ...
      ingress:
        - from:
          - podSelector:
              matchLabels:
                role: client
      ...
    

Водночас, оскільки Podʼи з hostNetwork мають ті самі IP-адреси, що й вузли, на яких вони розміщені, їхні зʼєднання вважатимуться зʼєднаннями вузла. Наприклад, ви можете дозволити трафік від Podʼа з hostNetwork за допомогою правила ipBlock.

Що не можна зробити за допомогою мережевих політик (принаймні поки що)

У Kubernetes 1.37 наведена нижче функціональність відсутня в API NetworkPolicy, але ви можете реалізувати обхідні рішення за допомогою компонентів операційної системи (таких як SELinux, OpenVSwitch, IPTables тощо), технологій рівня 7 (контролери Ingress, реалізації Service Mesh) або контролерів допуску. Якщо ви лише починаєте знайомитися з мережевою безпекою в Kubernetes, варто зазначити, що наведені нижче сценарії користувачів поки що не можна реалізувати за допомогою API NetworkPolicy.

  • Примусове проходження внутрішньокластерного трафіку через спільний шлюз (для цього, ймовірно, найкраще підійде service mesh або інший проксі).
  • Будь-що, повʼязане з TLS (для цього використовуйте service mesh або контролер Ingress).
  • Політики для окремих вузлів (для цього можна використовувати нотацію CIDR, але не можна націлитися на вузли саме за їхніми ідентифікаторами в Kubernetes).
  • Націлювання на Services за назвою (проте ви можете націлюватися на Podʼи чи простори імен за їхніми мітками, що часто є прийнятним обхідним рішенням).
  • Створення запитів на політики («Policy requests»), які виконує третя сторона, або керування ними.
  • Стандартні політики, які застосовуються до всіх просторів імен чи Podʼів (деякі сторонні дистрибутиви Kubernetes і проєкти можуть це робити).
  • Розширені запити до політик та інструменти перевірки досяжності.
  • Можливість логування подій мережевої безпеки (наприклад, заблокованих чи прийнятих зʼєднань).
  • Можливість явно забороняти політиками (наразі модель NetworkPolicies — стандартна заборона з можливістю додавати лише дозвільні правила).
  • Можливість заборонити loopback-трафік або вхідний трафік від хоста (Podʼи наразі не можуть блокувати доступ через localhost, а також не можуть блокувати доступ від вузла, на якому вони розміщені).

Вплив NetworkPolicy на наявні зʼєднання

Коли змінюється набір NetworkPolicies, що застосовуються до наявного зʼєднання, — через зміну самих NetworkPolicies або через зміну відповідних міток просторів імен чи Podʼів, вибраних політикою (і субʼєкта, і його співрозмовників (peers)), посеред наявного зʼєднання, — те, чи вплине ця зміна на це наявне зʼєднання, визначається реалізацією. Приклад: створено політику, яка призводить до заборони раніше дозволеного зʼєднання; реалізація базового мережевого втулка відповідає за визначення того, чи закриє ця нова політика наявні зʼєднання. Рекомендується не змінювати політики, Podʼи чи простори імен у спосіб, який може вплинути на наявні зʼєднання.

Що далі

Змінено November 01, 2026 at 6:40 PM PST: [uk] Add services-networking/network-policies.md (f97e03016b)