Showing posts with label Red Hat Device Edge. Show all posts
Showing posts with label Red Hat Device Edge. Show all posts

Tuesday, August 15, 2023

Bandwidth Limiting at The Edge


Recently I worked with a customer concerned about bandwidth, image replication and their edge locations. The customers concerns were warranted because they wanted to mirror a large set of software images to the edge sites but the connectivity to those edge sites while consistent was not necessarily the best for moving large data. Further to compound the problem the connectivity also was shared for other data transmitting services that the edge site relied on during daily business operations. The customer initially requested we add bandwidth capabilities to the software tooling that would be moving the images to the site. While at first glance this would seem to solve the issue it made me realize this might not be a scalable or efficient solution as tools change or as other software requirements for data movement evolve. Understanding the customers requirements and limitations at hand I approach this problem using some tools that are already built into Red Hat Device Edge and Red Hat OpenShift Container Platform. The rest of this blog will explore and demonstrate those options depending on the customers use case being: kubernetes container, non kubernetes container or a standard vanilla process on Linux.

OpenShift Pod Bandwidth Limiting

For OpenShift limiting ingress/egress bandwidth is fairly straight forward given Kubernetes traffic shaping capabilities. In the examples below we will run a basic Red Hat Universal Base Image container two different ways. One way will have no bandwidth restrictions and the other one will have bandwidth restrictions. Then inside each running container we can issue a curl command pulling the same file and see how the behavior differs. It is assumed this container would be the application container issuing the commands at the customer edge location.

Below lets create the normal pod running with no restrictions by first creating the custom resource file and then creating it on the OpenShift cluster.

$ cat << EOF > ~/edgepod-normal.yaml kind: Pod apiVersion: v1 metadata: name: edgepod-normal namespace: default labels: run: edgepod-normal spec: restartPolicy: Always containers: - resources: {} stdin: true terminationMessagePath: /dev/termination-log stdinOnce: true name: testpod-normal imagePullPolicy: Always terminationMessagePolicy: File tty: true image: registry.redhat.io/ubi9/ubi:latest args: - sh EOF $ oc create -f ~/edgepod-normal.yaml Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "testpod-normal" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "testpod-normal" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "testpod-normal" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "testpod-normal" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost") pod/edgepod-normal created $ oc get pods NAME READY STATUS RESTARTS AGE edgepod-normal 1/1 Running 0 5s

Now that we have our normal container running let's go ahead and create the same custom resource file and container with bandwidth restrictions. The custom resource file will be identical to the original one with the exception of the annotations we add around bandwidth for ingress and egress. For the bandwidth we will be restricting to 10M in this example.

$ cat << EOF > ~/edgepod-slow.yaml kind: Pod apiVersion: v1 metadata: name: edgepod-slow namespace: default labels: run: edgepod-normal annotations: { "kubernetes.io/ingress-bandwidth": "10M", "kubernetes.io/egress-bandwidth": "10M" } spec: restartPolicy: Always containers: - resources: {} stdin: true terminationMessagePath: /dev/termination-log stdinOnce: true name: testpod-normal imagePullPolicy: Always terminationMessagePolicy: File tty: true image: registry.redhat.io/ubi9/ubi:latest args: - sh EOF $ oc create -f ~/edgepod-slow.yaml Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "testpod-normal" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "testpod-normal" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "testpod-normal" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "testpod-normal" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost") pod/edgepod-slow created $ oc get pods NAME READY STATUS RESTARTS AGE edgepod-normal 1/1 Running 0 4m14s edgepod-slow 1/1 Running 0 3s

Now that both containers are up running let's go into edgepod-normal and run our baseline test curl command.

$ oc exec -it edgepod-normal /bin/bash kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. [root@edgepod-normal /]# curl http://192.168.0.29/images/discovery_image_agx.iso -o test.iso % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 909M 100 909M 0 0 115M 0 0:00:07 0:00:07 --:--:-- 128M

We can see from the results above that we were able to transfer the 909M file in ~7 seconds with a speed of 128M. Let's run the same command inside our edgepod-slow pod.

$ oc exec -it edgepod-slow /bin/bash kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. [root@edgepod-slow /]# curl http://192.168.0.29/images/discovery_image_agx.iso -o test.iso % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 909M 100 909M 0 0 1107k 0 0:14:01 0:14:01 --:--:-- 1151k

We can see from the results above that our annotation for bandwidth restricted the container to roughly a 10M speed and it took ~14 minutes to transfer the same 909M file. So if we think back to my customers use case this could be an option for restricting a containers traffic if they are using OpenShift.

Red Hat Enterprise Linux Bandwidth Limiting

In the previous section we looked at how OpenShift can bandwidth limit certain containers running in the cluster. Since the edge has a variety of customers and use cases let's explore how to do the same bandwidth restrictions from a non-kubernetes perspective. We will be using Traffic Control (tc) which is a very useful Linux utility that gives us the ability to control and shape traffic in the kernel. This tool normally ships with a variety of Linux distributions. In our demonstration environment we will be using Red Hat Enterprise Linux 9 since that is the host I have up and running.

First let's go ahead and create a container called edgepod using the ubi9 image.

$ podman run -itd --name edgepod ubi9 bash Resolved "ubi9" as an alias (/etc/containers/registries.conf.d/001-rhel-shortnames.conf) Trying to pull registry.access.redhat.com/ubi9:latest... Getting image source signatures Checking if image destination supports signatures Copying blob d6427437202d done Copying config 05936a40cf done Writing manifest to image destination Storing signatures 906716d99a39c5fc11373739a8aa20e192b348d0aaab2680775fe6ccc4dc00c3

Now let's go ahead and validate that the container is up and running.

$ podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 906716d99a39 registry.access.redhat.com/ubi9:latest bash 8 seconds ago Up 9 seconds edgepod

Once the container is up and running let's go ahead and run a baseline image pull inside the container to confirm how long it takes to pull the image. We will use the same image we pulled in the OpenShift example above for test.

$ podman exec -it edgepod curl http://192.168.0.29/images/discovery_image_agx.iso -o test.iso % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 909M 100 909M 0 0 52.8M 0 0:00:17 0:00:17 --:--:-- 52.9M ~

We can see from the results above that it took again about ~17 seconds to bring the 909M image over from the source. Now keep in mind this is our baseline.

Next we need to configure the Intermediate Functional Block (ifb) interface on the Red Hat Enterprise Linux host. The ifb pseudo network interface acts as a QoS concentrator for multiple different sources of traffic. We need to use this because tc only works on egress traffic on a real interface and the traffic we are trying to slow down is ingress traffic. To get started we need to load the module into the kernel. We will set the numifbs to one because the default is two and I just need one for my single interface. Once we load the module we can then set the link of the device to up and then confirm the interface is running.

$ sudo modprobe ifb numifbs=1 $ sudo ip link set dev ifb0 up $ sudo ip address show ifb0 5: ifb0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 32 link/ether b6:5c:67:99:2c:82 brd ff:ff:ff:ff:ff:ff inet6 fe80::b45c:67ff:fe99:2c82/64 scope link valid_lft forever preferred_lft forever

Now that the ifb interface is up we need to go ahead and apply some tc rules. The rules are performing the following functions in order:

  • Create an egress filter on the ifb0 device
  • Add root class htb with rate limiting of 1mbps
  • Create a matchall filter to classify all the traffic that runs on the port
  • Create ingress on external interface enp1s0
  • Forward all ingress traffic from enp1s0 to the ifb0 device
$ sudo tc qdisc add dev ifb0 root handle 1: htb r2q 1 $ sudo tc class add dev ifb0 parent 1: classid 1:1 htb rate 1mbps $ sudo tc filter add dev ifb0 parent 1: matchall flowid 1:1 $ sudo tc qdisc add dev enp1s0 handle ffff: ingress $ sudo tc filter add dev enp1s0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

Now we have our bandwidth limiting capabilities configured let's run our test again and see our results.

$ podman exec -it edgepod curl http://192.168.0.29/images/discovery_image_agx.iso -o test.iso % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 909M 100 909M 0 0 933k 0 0:16:38 0:16:38 --:--:-- 872k

We can see that with our tc rules applied the image was transferred at a much slower rate as expected which would again ensure we are not using all the bandwidth if this were an edge site. Now this might leave some wondering isn't this being applied to the whole system. The answer is yes but if there is not a system wide requirement and only maybe a certain job or task that needs to be rate limited we could wrap the commands into a script, execute the process at hand (our curl command in this example) and then remove the rules with the commands below.

$ tc qdisc del dev enp1s0 ingress $ tc qdisc del dev ifb0 root

And for sanity sake let's just run our command one more time to confirm we returned to baseline speeds.

$ podman exec -it edgepod curl http://192.168.0.29/images/discovery_image_agx.iso -o test.iso % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 909M 100 909M 0 0 52.0M 0 0:00:17 0:00:17 --:--:-- 46.9M

Hopefully this gives anyone working in edge environments with constrained bandwidth requirements ideas on how they can control certain processes and/or containers from using all the available bandwidth on the edge link. There are obviously a lot of other ways to use these concepts to further enable the most efficient use of the bandwidth availability at the edge but we will save that for some other time.

Wednesday, May 10, 2023

Experimental FIPS on Red Hat Device Edge with MicroShift

Federal agencies purchasing cryptographic-based security systems must confirm an associated FIPS 140-2 or FIPS 140-3 certificate exists. This procurement “check-box” can definitely be a deal breaker. The claim of “designed for FIPS” or “FIPS ready” are not sufficient to pass this requirment. If FIPS certification does not exist it will often mean there will be no sale with the vendor of choice. Many commercial and private organizations will also perceive a product as having an advantage when paired with the FIPS certification. From a Red Hat Exterprise Linux perspective whether a product is FIPS certified or not can be found in the following knowledge base article. The rest of this blog will detail how to technically install and show that FIPS is in use with a Red Hat Device Edge 9.2 rpm-ostree image that contains MicroShift 4.13.  Bear in mind as indicated in the above knowledge base article Red Hat Enterprise Linux 9.2 still does need to go through the proper 140-3 FIPS certification process.  What is provided here is merely to demonstrate forward looking thinking when FIPS compliance is achieved.

To get familiar with Red Hat Device Edge and MicroShift please reference the following blog post. We will use the same steps to build and produce the images for our FIPS experiment from that previous blog. The only difference will be that instead of using Red Hat Device Edge 8.7 and MicroShift 4.12 we are using the newer releases of those components. Keep in mind though that for builingd the rpm-ostree image we need to reposync down the updated Fast Data Path and Red Hat OpenShift repositories for the new versions. Also for packing the rpm-ostree into the boot.iso with the recook script we have to update the references of 8.7 to 9.2 which include boot.iso name and disk labels. Otherwise everything else can use the same logical steps.

FIPS mode on Red Hat Device Edge begins just like with Red Hat Enterprise Linux at install time when we boot the media that will allow us to install the system. In our case we will take the rhde-ztp.iso image we created in the blog referenced above and use that as our deployment image on our device. When we power on the device however we need to edit the kernel boot arguments and add the following fips=1 argument at the end.

linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-9-2-0-BaseOS-x86_64 inst.ks=hd:LABEL=RHEL-9-2-0-BaseOS-x86_64:/ks.cfg fips=1

We could also do this permanently in our grub.cfg file we create in the blog referenced above before we generate the rhde-ztp.iso.

set default="1"

function load_video {
  insmod efi_gop
  insmod efi_uga
  insmod video_bochs
  insmod video_cirrus
  insmod all_video
}

load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2

set timeout=60
### END /etc/grub.d/00_header ###

search --no-floppy --set=root -l 'RHEL-9-2-0-BaseOS-x86_64'

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Install Red Hat Enterprise Linux 9.2' --class fedora --class gnu-linux --class gnu --class os {
	linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-9-2-0-BaseOS-x86_64 inst.ks=hd:LABEL=RHEL-9-2-0-BaseOS-x86_64:/ks.cfg fips=1
	initrdefi /images/pxeboot/initrd.img
}
menuentry 'Test this media & install Red Hat Enterprise Linux 9.2' --class fedora --class gnu-linux --class gnu --class os {
	linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-9-2-0-BaseOS-x86_64 rd.live.check fips=1
	initrdefi /images/pxeboot/initrd.img
}
submenu 'Troubleshooting -->' {
	menuentry 'Install Red Hat Enterprise Linux 9.2 in text mode' --class fedora --class gnu-linux --class gnu --class os {
		linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-9-2-0-BaseOS-x86_64 inst.text quiet
		initrdefi /images/pxeboot/initrd.img
	}
	menuentry 'Rescue a Red Hat Enterprise Linux system' --class fedora --class gnu-linux --class gnu --class os {
		linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=RHEL-9-2-0-BaseOS-x86_64 inst.rescue quiet
		initrdefi /images/pxeboot/initrd.img
	}
}

Once the system boots from the rhde-ztp.iso with the FIPS argument the installation will proceed using the appropriate FIPS cryptography libraries and the rpm-ostree image will be applied to the device. Once the system reboots, MicroShift will start automatically and we can begin the process of verifying that indeed FIPS mode is enabled not only at the Red Hat Device Edge layer but also within a container that is deployed on MicroShift.

Now let's begin validating that FIPS is indeed running on the edge device. First lets login to the host and check from the Red Hat Device Edge perspective:

# cat /etc/redhat-release
Red Hat Enterprise Linux release 9.2 (Plow)
# fips-mode-setup --check
FIPS mode is enabled.

From the OS level FIPS is indeed enabled. Now let's also look at the openssl libraries installed on the edge device:

# rpm -qa|grep openssl
openssl-libs-3.0.7-6.el9_2.x86_64
openssl-3.0.7-6.el9_2.x86_64
xmlsec1-openssl-1.2.29-9.el9.x86_64

Next let's confirm MicroShift is up and running on the FIPS enabled edge device.

# export KUBECONFIG=/var/lib/microshift/resources/kubeadmin/kubeconfig
# oc get pods -A
NAMESPACE                  NAME                                 READY   STATUS    RESTARTS      AGE
openshift-dns              dns-default-pmncq                    2/2     Running   0             13h
openshift-dns              node-resolver-qh67d                  1/1     Running   0             13h
openshift-ingress          router-default-6857569799-njdsx      1/1     Running   0             13h
openshift-ovn-kubernetes   ovnkube-master-s5dpl                 4/4     Running   0             13h
openshift-ovn-kubernetes   ovnkube-node-m2bv6                   1/1     Running   1 (13h ago)   13h
openshift-service-ca       service-ca-7f49b8c7f5-rbsgf          1/1     Running   0             13h
openshift-storage          topolvm-controller-f58fcd7cb-6sggd   4/4     Running   0             13h
openshift-storage          topolvm-node-ddkhj                   4/4     Running   0             13h

Next let's create a simple deployment yaml referencing the nodejs minimal image from Red Hat:

# cat << EOF > ~/node-ubi.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-deployment
  namespace: simple
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-deployment
    type: Recreate
  template:
    metadata:
      labels:
        app: simple-deployment
        deploymentconfig: simple-deployment
    spec:
      securityContext:
        seccompProfile:
          type: RuntimeDefault
      containers:
      - image: registry.access.redhat.com/ubi8/nodejs-16-minimal
        imagePullPolicy: Always
        name: simple-deployment
        command:
        - /bin/sh
        - -c
        - |
          sleep infinity
        resources: {}
EOF

Now we can create a namespace and then deploy the simple deployment yaml we created onto MicroShift:

# oc create namespace simple
namespace/simple created
# oc create -f node-ubi.yaml 
deployment.apps/simple-deployment created

Let's verify our simple deployment nodejs pod is running:

# oc get pods -A
NAMESPACE                  NAME                                 READY   STATUS    RESTARTS      AGE
openshift-dns              dns-default-pmncq                    2/2     Running   0             13h
openshift-dns              node-resolver-qh67d                  1/1     Running   0             13h
openshift-ingress          router-default-6857569799-njdsx      1/1     Running   0             13h
openshift-ovn-kubernetes   ovnkube-master-s5dpl                 4/4     Running   0             13h
openshift-ovn-kubernetes   ovnkube-node-m2bv6                   1/1     Running   1 (13h ago)   13h
openshift-service-ca       service-ca-7f49b8c7f5-rbsgf          1/1     Running   0             13h
openshift-storage          topolvm-controller-f58fcd7cb-6sggd   4/4     Running   0             13h
openshift-storage          topolvm-node-ddkhj                   4/4     Running   0             13h
simple                     simple-deployment-66b9457cb9-v22vj   1/1     Running   0             54s

With the pod running we now need to get into a bash prompt in the simple deployment nodejs pod:

# oc exec -it simple-deployment-66b9457cb9-v22vj -n simple /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
bash-4.4$ 

From the command prompt in the pod let's first check the openssl libraries:

bash-4.4$ rpm -qa|grep openssl
openssl-libs-1.1.1k-9.el8_7.x86_64
openssl-1.1.1k-9.el8_7.x86_64

Now let's run a few commands to show that FIPS is indeed enabled in the OpenSSL libraries:

bash-4.4$ openssl version
OpenSSL 1.1.1k  FIPS 25 Mar 2021
bash-4.4$ node -p 'crypto.getFips()'
1
bash-4.4$ node -p 'crypto.createHash("md5")'
node:internal/crypto/hash:71
  this[kHandle] = new _Hash(algorithm, xofLen);
                  ^

Error: error:060800C8:digital envelope routines:EVP_DigestInit_ex:disabled for FIPS
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:130:10)
    at [eval]:1:8
    at Script.runInThisContext (node:vm:129:12)
    at Object.runInThisContext (node:vm:313:38)
    at node:internal/process/execution:79:19
    at [eval]-wrapper:6:22
    at evalScript (node:internal/process/execution:78:60)
    at node:internal/main/eval_string:27:3 {
  library: 'digital envelope routines',
  function: 'EVP_DigestInit_ex',
  reason: 'disabled for FIPS',
  code: 'ERR_OSSL_EVP_DISABLED_FOR_FIPS'
}

Observing the output from the three commands we can see the following:

  • OpenSSL version shows we are using FIPS
  • When we run the node command and try to get FIPS crypto we get a state of 1 which indicates enabled
  • Finally when we try to use node command and create a md5 hash we are told we cannot use it due to FIPS being enabled

This confirms that not only from the Red Hat Device Edge OS perspective but also within MicroShift that FIPS is indeed enabled. Thus ensuring that the standards can technically be configured and deployed on an rpm-ostree type image for Red Hat Device Edge devices.