Host-based Firewalls
This topic discusses the host-based firewall feature introduced in Contrail Networking Release 2003.
Host-based Firewalls Overview
Contrail Networking Release 2003 provides beta support for the host-based firewall feature which enables the creation of next generation firewalls using cSRX devices. Next-generation firewalls provide the ability to filter packets based on applications. They provide deep-packet inspection with intrusion detection and prevention at the application level.
Historically the vRouter has supported creation of regular Layer 4 firewall policies. To create Layer 7 application-level firewall policies, Contrail Networking uses service chaining. However, service chaining works only in cases of inter-virtual network traffic and not for intra-virtual network traffic. The host-based firewall feature offers next-generation firewall functions for traffic originating and ending in the same virtual network as well as in different networks. It uses the bump in the wire mode where the firewall instance does not change the packet format or Layer 2 header but applies Layer 7 policies on the packet.
Additionally, the host-based firewall feature uses tag-based policies to steer traffic. Tags are a simple and intuitive way of applying firewall intents and have the power to span multiple VNs, scale better, and can be attached at a VMI level as opposed to service chains. You can steer traffic towards the host-based firewall instance using tag-based policies. Policies are used to steer only specific traffic since the host-based firewall instance requires a fair amount of compute resources.
Also, host-based firewalls provide next-generation firewall functions closer to the workloads and can integrate with third-party firewall features.
Deploying Host-based Firewalls
Perform the following steps to deploy a host-based firewall. In this example we use Kubernetes as the orchestration platform since Kubernetes provides the flexibility to instantiate host-based firewall instances on selected compute nodes. The high-level list of steps are as follows:
Prerequisites
Install a Contrail-Kubernetes setup by using either contrail-ansible deployer or Contrail Command. See Provisioning of Kubernetes Clusters or Installing Standalone Kubernetes Contrail Cluster using the Contrail Command UI.
Topology
Consider the following sample Contrail-Kubernetes topology and instances.yml file.
Sample instances.yaml file
deployment: orchestrator: kubernetes deployer: contrail-ansible-deployer provider_config: bms: ssh_pwd: <password> ssh_user: username ntpserver: <IP NTP server> domainsuffix: <domain-suffix> instances: server1: provider: bms ip: 10.xx.xx.52 roles: config_database: config: control: analytics_database: analytics: webui: k8s_master: kubemanager: server2: provider: bms ip: 10.xx.xx.53 roles: k8s_node: vrouter: VROUTER_GATEWAY: 77.xx.x.100 global_configuration: CONTAINER_REGISTRY: 50.xx.xx.50:5000 REGISTRY_PRIVATE_INSECURE: True contrail_configuration: CONTRAIL_VERSION: <contrail-version> CLOUD_ORCHESTRATOR: kubernetes CONTROLLER_NODES: 10.xx.xx.52 CONTROL_NODES: 77.xx.xx.20 KUBERNETES_API_NODES: 77.xx.xx.20 KUBERNETES_API_SERVER: 77.xx.xx.20 CONTAINER_REGISTRY: 50.xx.xx.50:5000 REGISTRY_PRIVATE_INSECURE: True VROUTER_GATEWAY: 77.xx.x.100
Deployment Instructions
Procedure
Step-by-Step Procedure
To deploy a host-based firewall.
Create a namespace in Kubernetes. The namespace creates an equivalent project in Contrail.
Step-by-Step Procedure
Create a namespace.
kubectl create namespace hbf
Enable isolation on the namespace.
kubectl annotate namespace hbf "opencontrail.org/isolation"="true"
Verify namespace creation.
kubectl get ns hbf NAME STATUS AGE hbf Active 2d5h
Label the compute nodes for the host-based firewall function.
Step-by-Step Procedure
Get the list of compute nodes.
kubectl get nodes NAME STATUS ROLES AGE VERSION Server2 Ready <none> 16d v1.12.9 server1 NotReady master 16d v1.12.9
Select the nodes for the host-based firewall function and label them.
kubectl label node server type=hbf
Where server is the Kubernetes node name and hbf is the label.
Create a Kubernetes secret object in the namespace created earlier to pull the cSRX image.
kubectl create secret docker-registry hbf --docker-server=hub.juniper.net/security --docker-username=testuser --docker-password=testpassword -n hbf
Create a hbs object in the previously created namespace.
Step-by-Step Procedure
Create a python file with the following content and use the following command on the config_api Docker container.
docker exec -it config_api_1 bash (config-api)[root@server /]$ python hbs.py
cat hbs.py ---------------- from vnc_api import * from vnc_api.vnc_api import VncApi from vnc_api.vnc_api import Project from vnc_api.vnc_api import HostBasedService from vnc_api.exceptions import NoIdError from vnc_api.gen.resource_xsd import QuotaType hbs_name = ‘hbs’ # any other user defined name can be given project_name = ‘k8s-hbf’ # k8s is the default cluster name # user can change according to their admin_user = 'admin' admin_password = '<admin-password>' admin_project = 'admin' api_node_ip = '10.xx.xx.52' # change according to your topology api_node_port = '8082' # config api port default_domain = 'default-domain' if __name__ == "__main__": api = VncApi( username=admin_user, password=admin_password, tenant_name=admin_project, api_server_host=api_node_ip, api_server_port=api_node_port) '''Creates a project using vnc apis if it doesn’t exist already and enable hbf, if it’s not enabled already''' try: project = api.project_read(fq_name=[default_domain, project_name]) except NoIdError: project = Project(name=project_name) puuid = api.project_create(project) project = api.project_read(fq_name=[default_domain, project_name]) project.set_quota(QuotaType(host_based_service=1)) api.project_update(project) try: hbs = api.host_based_service_read(fq_name=project.fq_name + [hbs_name]) hbs_created = False except NoIdError: hbs = HostBasedService(hbs_name, parent_obj=project) hbs_created = True if hbs_created: api.host_based_service_create(hbs) else: api.host_based_service_update(hbs)
Create a daemonset for the host-based firewall instances. By default, host-based firewall instances run on all compute nodes. You can choose to run host-based firewall instances on specific compute nodes only by labeling them as shown in 2.b. The host-based firewall instance has three interfaces. The traffic flows in to the left interface and firewall functions are performed on the packets and traffic flows out of the right interface. The management interface is the default pod network.
Step-by-Step Procedure
Generate a ds.yaml file as shown in the following example to create a daemonset with the cSRX container image. The left and right interfaces are automatically created and link to the hbs object with 'left' and 'right' so that traffic flows marked for the host-based firewall are steered through the cSRX device. Note that, Kubernetes objects names and values can be changed as per your requirement.
cat ds.yaml ----------- apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: annotations: opencontrail.org/network: '{"domain":"default-domain", "project":"k8s-hbf", "name":"__hbs-hbf-left__"}' name: left namespace: hbf spec: config: '{"cniVersion":"0.3.0", "type": "contrail-k8s-cni" }' --- apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: annotations: opencontrail.org/network: '{"domain":"default-domain", "project":"k8s-hbf", "name":"__hbs-hbf-right__"}' name: right namespace: hbf spec: config: '{"cniVersion":"0.3.0", "type": "contrail-k8s-cni" }' --- apiVersion: apps/v1 kind: DaemonSet metadata: labels: type: hbf name: hbf namespace: hbf spec: selector: matchLabels: type: hbf template: metadata: annotations: k8s.v1.cni.cncf.io/networks: '[{"name":"left"}, {"name":"right"}]' labels: type: hbf spec: containers: - env: - name: CSRX_FORWARD_MODE value: wire image: hub.juniper.net/security/csrx:19.2R1.8 name: csrx securityContext: privileged: true stdin: true tty: false imagePullSecrets: - name: hbf nodeSelector: type: hbf restartPolicy: Always
Create the Kubernetes objects which will in turn create a cSRX pod with the left and right interfaces on each compute node for each namespace.
kubectl create -f ds.yaml
Verify the objects, daemonset, network attachment definitions, and the cSRX pods.
kubectl get ds -n hbf NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE hbf 1 1 1 1 1 type=hbf 34h kubectl get network-attachment-definitions -n hbf NAME AGE left 34h right 34h [root@nodeg12 ~]# kubectl get pods -n hbf NAME READY STATUS RESTARTS AGE csrx-75ncs 1/1 Running 0 34h csrx-9qxl7 1/1 Running 0 34h
Configure the cSRX pods of the daemonset on each compute node with the following configuration.
set interfaces ge-0/0/0 unit 0 set interfaces ge-0/0/1 unit 0 set security policies default-policy permit-all set security zones security-zone trust interfaces ge-0/0/0.0 set security zones security-zone untrust interfaces ge-0/0/1.0 commit
Create a network policy between left and right interfaces using the vnc API or through Contrail Command.
Network policies are necessary only for inter-virtual network traffic and not for intra-virtual network traffic.
docker exec -it config_api_1 bash (config-api)[root@server /]$ python nwp.py cat nwp.py ========== from vnc_api import * from vnc_api import vnc_api from vnc_api.vnc_api import VncApi from vnc_api.vnc_api import Project from vnc_api.exceptions import NoIdError policy_name = 'allow-left-right'. # any policy name if __name__ == "__main__": vn_left = api.virtual_network_read(fq_name= \ [default_domain, project_name, 'k8s-left-pod-network']) vn_right = api.virtual_network_read(fq_name= \ [default_domain, project_name, 'k8s-right-pod-network']) policy_obj = vnc_api.NetworkPolicy(policy_name, network_policy_entries= \ vnc_api.PolicyEntriesType([vnc_api.PolicyRuleType(direction='<>', \ action_list=vnc_api.ActionListType(simple_action='pass'), \ protocol='any', \ src_addresses=[vnc_api.AddressType( virtual_network=vn_left.get_fq_name_str())], \ src_ports=[vnc_api.PortType(-1, -1)], \ dst_addresses=[vnc_api.AddressType( virtual_network=vn_right.get_fq_name_str())], \ dst_ports=[vnc_api.PortType(-1, -1)])]), \ parent_obj=project) api.network_policy_create(policy_obj) vn_left.add_network_policy(policy_obj, \ vnc_api.VirtualNetworkPolicyType(sequence=vnc_api.SequenceType(0, 0))) vn_right.add_network_policy(policy_obj, \ vnc_api.VirtualNetworkPolicyType(sequence=vnc_api.SequenceType(0, 0))) api.virtual_network_update(vn_left) api.virtual_network_update(vn_right)
Create a firewall policy and enable host-based firewall for the firewall rules.
Create tags, application policy sets (APS), as well as create Firewall Policy and Firewall Rule under project scoped rules. Enable host-based firewall on the firewall rules and set host_based_service = True.
cat add_hbs_fr.py ------------------ from vnc_api import * from vnc_api import vnc_api from vnc_api.vnc_api import VncApi from vnc_api.vnc_api import Project from vnc_api.vnc_api import HostBasedService from vnc_api.vnc_api import FirewallRule, FirewallServiceType, PortType, FirewallRuleEndpointType from vnc_api.exceptions import NoIdError from vnc_api.vnc_api import ActionListType if __name__ == "__main__": try : rule = api.firewall_rule_read(fq_name=fr_fq_name) #fr_fq_name = [default_domain,project_name,<firewall rule> except NoIdError: rule = api.firewall_rule_create(rule_obj) rule = api.firewall_rule_read(rule_obj) rule.set_action_list( ActionListType(host_based_service=True,simple_action="pass")) api.firewall_rule_update(rule) fwp.add_firewall_rule(rule)
When the traffic goes through the host-based firewall, the cSRX on the corresponding compute nodes creates the host-based firewall flow and respective sessions.
vrouter-agent)[root@nodec61 /]$ flow -l --match 1.xx.xx.251 Flow table(size 161218560, entries 629760) Listing flows matching ([1.xx.xx.251]:*) Index Source:Port/Destination:Port Proto(V) ------------------------------------------------------------------------------------------------------------------- 320632<=>327760 1.xx.xx.251:407 1 (8->12) 2.xx.xx.251:0 (Gen: 4, K(nh):95, Action:F, Flags:, QOS:-1, S(nh):95, Stats:21/2058,SPort 49640, TTL 0, HbsLeft, Sinfo 12.0.0.0) 327760<=>320632 2.xx.xx.251:407 1 (8->12) 1.xx.xx.251:0 (Gen: 3, K(nh):95, Action:F, Flags:, QOS:-1, S(nh):24, Stats:20/1680, SPort 61330, TTL 0, HbsRight, Sinfo 77.xx.xx.21) On csrx ======== root@csrx-7vhn6> show security flow session Session ID: 76342, Policy name: default-policy-logical-system-00/2, Timeout: 4, Valid In: 2.xx.xx.251/407 --> 1.xx.xx.251/128;icmp, Conn Tag: 0x0, If: ge-0/0/1.0, Pkts: 1, Bytes: 98, Out: 1.xx.xx.251/128 --> 2.xx.xx.251/407;icmp, Conn Tag: 0x0, If: ge-0/0/0.0, Pkts: 0, Bytes: 0, Total sessions: 1