Channel Access gateway setup

The Channel Access (CA) gateway is a program that acts as gateway, which enables client from a network to access IOCs on another network.

Setting up a CA gateway also enables you to add extra access security rules on top of IOCs.

For more details and documentation about the CA PV gateway, you can examine the gateway main page.

Pre-requisites

  • Having a NixOS machine with a flake configuration.

If you’re not sure how to do this, you can follow the Creating an Archiver Appliance instance tutorial, which is a good introduction on how to make a NixOS VM.

If you have such a configuration, make sure that:

  • You have the epnix flake input

  • You have added epnix as an argument to your flake outputs

  • You have imported EPNix’ NixOS module

For example:

flake.nix
 {
   # ...
   inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
+  inputs.epnix.url = "github:epics-extensions/EPNix/nixos-24.05";

   # ...
   outputs = {
     self,
     nixpkgs,
+    epnix,
   }: {
     nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
       modules = [
+        epnix.nixosModules.nixos

         # ...
       ];
     };
   };
 }

Enabling the gateway

To enable the gateway, add this to your configuration:

ca-gateway.nix
{
  services.ca-gateway = {
    enable = true;
    openFirewall = true;
  };
}

This configuration starts the CA gateway in a ca-gateway.service systemd service. In this configuration, the gateway listens on all interface with a broadcast IP address, and forwards all Channel Access request.

The openFirewall option opens the 5064 TCP, 5064 UDP, and 5065 UDP ports on all network interfaces.

Firewall on specific interfaces

If you want to enable the firewall on specific interfaces, you can remove the openFirewall option and configure the firewall manually.

You can also use the cip setting to specify where the gateway should listen for the CA server part.

For example:

ca-gateway.nix
{config, ...}: {
  services.ca-gateway = {
    enable = true;
    # Server side listen address
    # Let's say this IP address is on enp0s2
    settings.sip = ["10.0.2.1"];

    # Let's also say the enp0s1 interface is
    # where communication with "real" IOCs happen (client side)

    # openFirewall is left as false by default
  };

  networking.firewall = let
    gwSettings = config.services.ca-gateway.settings;
  in {
    interfaces = {
      # Open the firewall on the interface from the client side of the gateway,
      # this will be the side of the gateway listening
      # for replies to beacons and PV search requests
      "enp0s1".allowedUDPPorts = [5065];

      # Open the firewall on the interface from the server side of the gateway,
      # this will be the side of the gateway listening for Channel Access requests
      "enp0s2" = {
        # Use the value of the `sport` setting
        allowedTCPPorts = [gwSettings.sport];
        allowedUDPPorts = [gwSettings.sport];
      };
    };

    # Allow incoming UDP packets with *source* port 5064,
    # from the client side of the gateway.
    # This is needed to listen to CA broadcast responses
    extraCommands = ''
      ip46tables -A nixos-fw -p udp --sport 5064 -j nixos-fw-accept -i enp0s1
    '';
  };
}

Filtering IOCs

By using the cip setting, you can filter which IOCs get exposed by the gateway. This is equivalent to setting the environment variable EPICS_CA_ADDR_LIST and setting EPICS_CA_AUTO_ADDR_LIST=NO.

For example:

ca-gateway.nix
{
  services.ca-gateway = {
    enable = true;
    # These IOCs get exposed by the gateway
    settings.cip = [
      "10.0.1.42"
      "10.0.1.69"

      # you can specify the port, too,
      # if your IOC listens on something other than 5064
      "10.0.1.237:5067"

      # domain names also work
      "myioc"
    ];
  };
}

Filtering process variables

By using the pvlist setting, you can filter which PVs get exposed by the gateway.

This option takes a file in the gateway pvlist format. See the GATEWAY.pvlist example on the ca-gateway repository. The list supports regular expressions (Perl style).

In the configuration

For example:

ca-gateway.nix
{pkgs, ...}: {
  services.ca-gateway = {
    enable = true;
    # These PVs get exposed by the gateway
    # This list implements an "allowlist":
    # DENY by default, some PVs explicitely ALLOW
    settings.pvlist = pkgs.writeText "gateway.pvlist" ''
      EVALUATION ORDER DENY, ALLOW

      .* DENY

      MY_PV1 ALLOW
      MY_PV2 ALLOW

      # Or:

      MY_PV[0-9]+ ALLOW
    '';
  };
}

In a separate file

For long lists, it can be better to put it in a separate file. You can do this by adding a gateway.pvlist in the same directory as your configuration:

EVALUATION ORDER DENY, ALLOW

.* DENY

MY_PV1 ALLOW
MY_PV2 ALLOW

# Or:

MY_PV[0-9]+ ALLOW

And in your configuration:

{
  services.ca-gateway = {
    enable = true;
    # Make sure that the value is *not* quoted
    settings.pvlist = ./gateway.pvlist;
  };
}