Blob Blame History Raw
#!/usr/bin/python3
"""
Configure firewall

Configure firewalld using the `firewall-offline-cmd` from inside the target.

This stage adds each of the given `ports` and `enabled_services` to the default
firewall zone using the `--port` and `--service` options, then removes the
services listed in `disabled_services` with `--remove-service`.

Ports should be specified as "portid:protocol" or "portid-portid:protocol",
where "portid" is a number (or a port name from `/etc/services`, like "ssh" or
"echo") and "protocol" is one of "tcp", "udp", "sctp", or "dccp".

Enabling or disabling a service that is already enabled or disabled will not
cause an error.

Attempting to enable/disable an unknown service name will cause this stage to
fail. Known service names are determined by the contents of firewalld's
configuration directories, usually `/{lib,etc}/firewalld/services/*.xml`, and
may vary from release to release.

WARNING: this stage uses `chroot` to run `firewall-offline-cmd` inside the
target tree, which means it may fail unexpectedly when the buildhost and target
are different arches or OSes.
"""


import json
import subprocess
import sys

SCHEMA = """
"additionalProperties": false,
"properties": {
  "ports": {
    "description": "Ports (or port ranges) to open",
    "type": "array",
    "items": {
      "type": "string",
      "description": "A port or port range: 'portid[-portid]:protocol'",
      "pattern": ".:(tcp|udp|sctp|dccp)$"
    }
  },
  "enabled_services": {
    "description": "Network services to allow in the default firewall zone",
    "type": "array",
    "items": {
      "type": "string",
      "description": "Service name (from /{lib,etc}/firewalld/services/*.xml)"
    }
  },
  "disabled_services": {
    "description": "Network services to remove from the default firewall zone",
    "type": "array",
    "items": {
      "type": "string",
      "description": "Service name (from /{lib,etc}/firewalld/services/*.xml)"
    }
  }
}
"""

def main(tree, options):
    # Takes a list of <port|application protocol>:<transport protocol> pairs
    ports = options.get("ports", [])
    # These must be defined for firewalld. It has a set of pre-defined services here: /usr/lib/firewalld/services/, but
    # you can also define you own XML files in /etc/firewalld.
    enabled_services = options.get("enabled_services", [])
    disabled_services = options.get("disabled_services", [])

    # firewall-offline-cmd does not implement --root option so we must chroot it
    subprocess.run(["chroot",
                    tree,
                    "firewall-offline-cmd"] +
                   list(map(lambda x: f"--port={x}", ports)) +
                   list(map(lambda x: f"--service={x}", enabled_services)) +
                   list(map(lambda x: f"--remove-service={x}", disabled_services)),
                   check=True)

    return 0


if __name__ == '__main__':
    args = json.load(sys.stdin)
    r = main(args["tree"], args["options"])
    sys.exit(r)