Oracle Cloud Infrastructure: using the CLI to manipulate Network Security Groups

I frequently need to update security rules in one of my Network Security Groups (NSG). Rather than logging into the console and clicking my way through the user interface to eventually change the rule I decided to give it a go and automate the process using the Oracle Cloud Infrastructure (OCI) Command Line Interface (CLI). It took me slightly longer than I thought to get it right, so hopefully this post saves you 5 minutes. And me, later, when I forgot how I did it :)

In my defense I should point out this isn’t one of the terraform controlled environments I use but rather a cloud playground with a single network, a few of subnets, Network Security Groups (NSG) and security lists that have grown organically. If that sounds similar to what you are doing, read on. If not, please use terraform to control the state of your cloud infrastructure, it’s much better suited to the task, especially when working with others. The rule is: “once terraform, always terraform” when making changes to the infrastructure.

I have used Ubuntu 20.04 LTS as a host for version 3.0.0 of the CLI, the current version at the time of writing. It’s assumed you already set the CLI up and have the correct access policies granted to you to make changes to the NSG. I also defined a default compartment in ~/.oci/oci_cli_rc so I don’t have to add a --compartment-id to every call to the CLI.

Listing Network Security Groups

The landing page for NSGs in OCI CLI was my starting point. The list and rules list/rules update verbs are exactly what I need.

Before I can list the security rules for a given NSG I need to find its Oracle Cloud ID (OCID) first:

(oracle-cli) [martin@ubuntu: ~]$ oci network nsg list \
> --query 'data[].{id:id,"display-name":"display-name" }' \
> --output table
+-----------------------+-------------------------------------------------...---+
| display-name          | id                                              ...   |
+-----------------------+-------------------------------------------------...---+
| NSG1                  | ocid1.networksecuritygroup.oc1.eu-frankfurt-1.aa...vq |
| NSG2                  | ocid1.networksecuritygroup.oc1.eu-frankfurt-1.aa...5q |
...
| NSG5                  | ocid1.networksecuritygroup.oc1.eu-frankfurt-1.aa...vq |
| NSG6                  | ocid1.networksecuritygroup.oc1.eu-frankfurt-1.aa...3a |
+-----------------------+-------------------------------------------------...---+
(oracle-cli) [martin@ubuntu: ~]$ 

The table provides me with a list of NSGs and their OCIDs.

Getting a NSG’s Security Rules

Now that I have the NSG’s OCID, I can list its security rules:

(oracle-cli) [martin@ubuntu: ~]$ oci network nsg rules list \
> --nsg-id ocid1.networksecuritygroup.oc1.eu-frankfurt-1.aa...

The result is a potentially looong JSON document, containing a data[] array with the rules and their metadata:

(oracle-cli) [martin@ubuntu: ~]oci network nsg rules list --nsg-id ocid1.networksecuritygroup.oc1.eu-frankfurt-1.aa...
{
  "data": [
    {
      "description": "my first rule",
...

Updating a Security Rule

As per the documentation, I need to pass the NSG OCID as well as security rules to oci network nsg rules update. Which makes sense when you think about it … There is only one small caveat: the security rules are considered a complex type (= JSON document). Rather than passing a string on the command line, the suggestion is to create a JSON document with the appropriate parameters, store it on the file system and pass it via the file://payload.json directive.

But what exactly do I have to provide as part of the update request? The first thing I did was to look at the JSON document produced by oci network nsg rules list to identify the rule and payload I need to update. The documentation wasn’t 100% clear whether I can update just a single security rule so I thought I’d just try it. The API documentation has details about the various properties as well as links to the TcpOptions and UdpOptions. Not all of these are always required, have a look at the documentation for details. Using all the available sources I ended up with the following in /tmp/payload.json:

[
    {
        "description": "my first SSH rule",
        "direction": "INGRESS",
        "id": "04ABEC",
        "protocol": "6",
        "source": "192.168.10.0/24",
        "source-type": "CIDR_BLOCK",
        "tcp-options": {
            "destination-port-range": {
                "max": 22,
                "min": 22
            }
        }
    }
]

The actual contents of the file varies from use case to use case, however there are a couple of things worth pointing out:

  • Even though I intend to update a single rule, I need to provide a JSON array (containing a single object, the rule)
  • The security rule must be valid JSON
  • You absolutely NEED an id, otherwise OCI can’t update the existing rule

With these things in mind you can update the rule:

(oracle-cli) [martin@ubuntu: ~]$ oci network nsg rules update \
> --nsg-id ocid1.networksecuritygroup.oc1.eu-frankfurt-1.aaa... \
> --security-rules file:///tmp/payload.json 
{
  "data": {
    "security-rules": [
      {
        "description": "my first rule",
        "destination": null,
        "destination-type": null,
        "direction": "INGRESS",
        "icmp-options": null,
        "id": "04ABEC",
        "is-stateless": false,
        "is-valid": true,
        "protocol": "6",
        "source": "192.168.10.0/24",
        "source-type": "CIDR_BLOCK",
        "tcp-options": {
          "destination-port-range": {
            "max": 22,
            "min": 22
          },
          "source-port-range": null
        },
        "time-created": "2020-11-23T14:24:55.363000+00:00",
        "udp-options": null
      }
    ]
  }
}

In case of success you are presented with a JSON document listing the updated rule(s).

Advertisement