Workflow Server Deployment

Table of contents

  1. IIS deploy
  2. Docker deploy with nginx as a reverse-proxy

IIS deploy

Table of contents

  1. To run an ASP.NET Core application under IIS, the installation of .NET Core Hosting Bundle is required. Since Workflow Server uses .NET Core 2.1, please, install the appropriate version using the link.

  2. First, we create a folder to contain the Workflow Server files. Then, we copy into this folder the following items: the backend and frontend folders from the workflowserver project with javascript code, and static files for the admin panel and the form manager. Since Workflow Server uses two web hosts, one for the admin panel and web API, and the other for the form manager, we have to create two IIS sites. Therefore, we create two folders to contain the assemblies of these sites: wfs_admin for the admin panel, and wfs_forms for the form manager.

    Fig1

  3. To create the IIS admin panel, please, launch the Internet Information Services (IIS) Manager and select “Add Website ...” from the Actions list.

    Fig2

  4. In the window that appears, the following fields should be filled:

  • Site name: wfs_admin

  • Physical path: the path to the wfs_admin folder, created in Step 2.

  • Port: a unallocated port through which the admin panel is available, for example 8077.

    Fig3

  1. The Application pool for the wfs_admin site should be set up to avoid unnecessary restarts of the Workflow Server runtime or shutdowns when idle. Thus, in the Internet Information Services (IIS) Manager, go to the Application Pools List and select the wfs_admin pool.

    Fig4

    Select Edit Application Pool -> Recycling, in the dialog box that appears, turn off the Regular time intervals (in minutes) option.

    Fig5

    Click Next -> Finish Select Edit Application Pool -> Advanced Settings In the window that appears, find the Process Model -> Idle time-out (minutes) option, and change its value from 20 to 0.

    Fig6

    Press OK, the wfs_admin pool is set up.

  2. Then, prepare the config.json file for the admin panel. In the default mode, Workflow Server starts 2 web hosts simultaneously (the admin panel and the form manager). This operation mode is incompatible with IIS, therefore we set the ServerType property to Admin, and change the BackendFolder property so that it contains the relative path from the wfs_admin folder to the backend folder copied in Step 2. Since we are going to launch 2 copies of the Workflow Server, we enable the Multiserver mode by setting the IsMultiServer property to true.

     {
       "BackendFolder": "../backend",
       "ServerType": "Admin",
       "IsMultiServer": true,
     }

    Please, note that the url property set in config.json is ignored; instead, the IIS binding sets for the wfs_admin site are used.

  3. Now, we publish Workflow Server for the wfs_admin site.

    • Using Visual Studio 2019.
      In the context menu of the WorkflowServer project, select Publish (or, select Build -> Publish WorkflowServer in the main menu)

      Fig7

      In the publishing window that appears, create a new profile by selecting "new":

      Fig8

      In the dialog box that appears, select Folder and press Next:

      Fig9

      As the Folder location, specify the path to the wfs_admin folder created in Step 2, and click Finish.

      Fig10

      Publish Workflow Server by pressing the Publish button.

    • Alternatively, publishing can be performed without Visual Studio, through the command line. In the workflowserver project folder, containing WorkflowServer.sln, run the command:

      dotnet publish WorkflowServer.sln -o <WFS_ADMIN_PATH>

      Where - the path to the wfs_admin folder created in Step 2.

    • If the wfs_admin folder is not available on the computer where the application is being developed, any available folder can be seleced for publishing; then, its contents should be copied to the wfs_admin folder.

  4. Then, we create the form manager IIS site and configure its Application pool as described in Steps 3-5. The site settings are:

    • Site name: wfs_forms
    • Physical path: the path to the wfs_forms folder created in Step 2.
    • Port: a free port through which the form manager is available, different from the admin panel port, for example 8078
  5. Now, we prepare the config.json file to publish the form manager. First, set the ServerType property to Forms, then, change the FrontendFolder property so that it contains the relative path from the wfs_forms folder to the frontend folder copied in Step 2. Since we are going to launch 2 copies of Workflow Server, we enable the Multiserver mode by setting the IsMultiServer property to true.

     {
       "FrontendFolder": "../frontend",
       "ServerType": "Forms",
       "IsMultiServer": true,
     }

    Please, note that the DefaultFrontendPort property set in the config.json file is ignored; instead, the ISS binding sets for the wfs_forms site are used.

  6. Now, we publish Workflow Server for the wfs_forms site, similarly to Step 7, using the wfs_forms folder created in Step 2 as the target for publishing.

  7. IIS deploy is done; both the admin panel and the form manager are accessible according to the ISS binding sets, specified for the wfs_admin site and the wfs_forms site respectively.

    Fig11

Docker deploy with nginx as a reverse-proxy

Table of contents

To deploy WFS with nginx as a reverse-proxy, we need 2 Docker containers at least; one for Workflow Server, and the other for nginx. To manage the containers, we use Docker Compose and take docker-files/docker-compose.yml from the workflowserver project as the basis solution for deploying Workflow Server and PostgreSql. This solution consists of the following three containers:

  • db – PostgreSql DBMS container
  • workflowserver – Workflow Server container
  • start_db – the container to prevent the workflowserver container from starting until PostgreSql is fully activated and ready to accept queries.

To correctly determine the IP address and client protocol in WFS when working through a reverse-proxy, the forwarded headers must be enabled. To configure forwarding, the ProxySettings property in ServerSettings is used as follows:

public class ProxySettings
{
  public List<string> KnownNetworks { get; set; } = new List<string>();
  public List<string> KnownProxies { get; set; } = new List<string>();
  public List<ForwardedHeaders> Headers { get; set; } = new List<ForwardedHeaders> { ForwardedHeaders.XForwardedFor, ForwardedHeaders.XForwardedProto };
  public bool Enabled { get; } = true;
}

Where Headers is the list of headers to be forwarded.Possible values are:

You can also see more info here

For security reasons, header forwarding should be restricted to trusted proxies only. You can set the trusted proxies using the following properties:

  • KnownNetworks - the list of trusted subnets in the CIDR notation (IP address / mask)
  • KnownProxies - the list of the IPs of trusted proxy servers.

ProxySettings parameters can be configured using both config.json and environment variables. For debugging purposes, ProxySettings can be used without specifying KnownNetworks or KnownProxies, then the headers are forwarded for any addresses. To enable this mode, set the environment variable ProxySettings__Enabled to true.

Warning! In production mode, ProxySettings without specifying KnownNetworks or KnownProxies are not recommended.

  1. We define the parameters of the subnet made for our containers. The subnet is created when the compose project is launched for the first time; thus, we start this project using startcontainer.bat or startcontainer.sh, and then stop it by pressing Ctrl+C.
    In the command prompt, execute the following:

     docker network inspect docker-files_default

    It shows the default network info of the workflowserver project.

    Fig12

    As you can see, in our case, the default network has 172.27.0.0/16 mask and 172.27.0.1 gateway. The containers are given dynamic addresses on creating. To set KnownProxies, we assign static addresses for the existing containers by adding a node to each service configuration:

     networks:
       default:
         ipv4_address: <IP_address>

    where - a unique IP-address within 172.27.0.2 - 172.27.1.254

    Fig13

  2. We create the configuration file nginx.conf in the folder docker-compose/postgresql. It contains the following:

     worker_processes 4;
    
     events { worker_connections 1024; }
    
     http {
         sendfile on;
    
         server {
             listen 8077;
             location / {
                 proxy_pass         http://workflowserver:8077;
                 proxy_http_version 1.1;
                 proxy_set_header   Upgrade $http_upgrade;
                 proxy_set_header   Connection $http_connection;
                 proxy_set_header   Host $host;
                 proxy_cache_bypass $http_upgrade;
                 proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
                 proxy_set_header   X-Forwarded-Proto $scheme;
             }
         }
    
         server {
             listen 8078;
             location / {
                 proxy_pass         http://workflowserver:8078;
                 proxy_http_version 1.1;
                 proxy_set_header   Upgrade $http_upgrade;
                 proxy_set_header   Connection $http_connection;
                 proxy_set_header   Host $host;
                 proxy_cache_bypass $http_upgrade;
                 proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
                 proxy_set_header   X-Forwarded-Proto $scheme;
             }
         }
     }

    Fig14

    The file describes the two servers corresponding to the two Workflow Server web hosts: Admin Panel/Web API and Form Manager.
    Line 9:

     listen 8077;

    It sets the external port to access Admin Panel/Web API.
    Line 11:

     proxy_pass         http://workflowserver:8077;

    It indicates the Admin Panel/Web API address in the internal network. The workflowserver host name corresponds to the service name in the docker-compose.yml configuration file, and the port number must match the url specified in config.json.
    Similarly, line 23 defines the external port to access Form Manager, and line 25 indicates the Form Manager address in the internal network (the port number must match DefaultFrontendPort specified in config.json).

  3. To remove the port from the workflowserver container, making it inaccessible from the external network, we delete these lines in docker-compose.yml:

     ports:
       - "8077:8077"
       - "8078:8078"

    Fig15

  4. We add the following description of the nginx container in docker-compose.yml:

     nginx:
        depends_on:
          - workflowserver
        image: nginx:latest
        volumes:
          - ./nginx.conf:/etc/nginx/nginx.conf
        ports:
          - "8077:8077"
          - "8078:8078"
        networks:
          default:
              ipv4_address: 172.27.0.5

    Fig16

  5. To add the nginx server IP-address to the list of KnownProxies, we add the following line in docker-compose.yml in the environment node of the workflowserver:

     ProxySettings__KnownProxies__0: 172.27.0.5

    Fig17

  6. docker-compose.yml is ready, the full listing is as follows:

     version: '3.4'
    
     services:
       db:
          image: postgres:alpine
          environment:
            POSTGRES_DB: wfs
            POSTGRES_USER: dbuser
            POSTGRES_PASSWORD: dbuserpassword
          volumes:
            - dbdata:/var/lib/postgresql/data
          restart: always
          ports:
            - "5432:5432"
          networks:
            default:
              ipv4_address: 172.27.0.2
       start_db:
         image: jbergknoff/postgresql-client
         depends_on:
            - db
         volumes:
            - ./docker-files:/home/.docker
         entrypoint: /home/.docker/wait-for-db.sh db dbuser dbuserpassword wfs
         networks:
           default:
             ipv4_address: 172.27.0.3
       workflowserver:
         depends_on:
            - db
         build:
           context: ../..
           dockerfile: ./WorkflowServer/Dockerfile
         volumes:
            - ./logs:/app/logs
            - ./license:/app/license
         environment:
           ConnectionString: HOST=db;User ID=dbuser;Password=dbuserpassword;Database=wfs;Port=5432
           Provider: postgresql
           LicensePath: /app/license/  
           DefaultLoggerConfig__FileTarget__0: Debug
           DefaultLoggerConfig__FileTarget__1: Information
           DefaultLoggerConfig__FileTarget__2: Error
           DefaultLoggerConfig__FileSettings__FileName: /app/logs/log.txt
           DefaultLoggerConfig__FileSettings__RollingInterval: Day
           DefaultLoggerConfig__FileSettings__RetainedFileCountLimit: 30
           ProxySettings__KnownProxies__0: 172.27.0.5
         networks:
           default:
             ipv4_address: 172.27.0.4
       nginx:
         depends_on:
           - workflowserver
         image: nginx:latest
         volumes:
           - ./nginx.conf:/etc/nginx/nginx.conf
         ports:
           - "8077:8077"
           - "8078:8078"
         networks:
           default:
               ipv4_address: 172.27.0.5
     volumes:
         dbdata:
  7. Now, we run the containers using any of the scripts: startcontainer.bat or startcontainer.sh.

    Fig18

    As seen in the logs, queries come from 172.27.0.1 (the client IP-address) to nginx_1(the reverse-proxy server) and successfully redirected to workflowserver_1, where Workflow Server is running. In turn, Workflow Server correctly defines the RemoteIPAddress of the client as 172.27.0.1.

Table of contents

Top