# 2.3 AWS ECS

## Deploy on AWS ECS

Deploying ZTrust SSO on ECS is possible by creating the tasks in the Elastic Container Service (ECS). ZTrust SSO is compatible on both Fargate (as a serverless container) and EC2 instance-powered ECS. Deploying ZTrust SSO on AWS ECS is straightforward and tightly integrated with AWS services, where workloads are defined as task definitions and run as services on either EC2 instances or serverless Fargate. ECS abstracts much of the orchestration complexity but is AWS-specific. Though you will observe minimal differences with defining secrets, environment variables and volume mounts on ECS, it all can be handled at the ECS task definitions.

### Prerequisites

* **AWS ECS Cluster**
  * VPC with at least 2 public subnets (for ALB) and 2 private subnets (for ECS tasks).
  * ZTrust SSO requires a minimum of 800 milicore CPUs and 600 MB of RAM during startup.
  * With a standard load, ZTrust consumes around 300 millicores of CPU and 1200 MB of RAM per pod.
  * We recommend allocating increased amount of VCPU & RAM to ECS cluster due to the additional tasks the container is going to perform, such as log & metrics collection.
  * ZTrust SSO is compatible both on ECS Fargate and EC2.
* **Database**
  * ZTrust needs a database to persist the ZTrust configuration and user data. ZTrust will not be able to start on ECS if the database is not connected. The following table shows a list of supported databases for ZTrust.

| Database                 | Option Value | Tested Version |
| ------------------------ | ------------ | -------------- |
| MariaDb                  | mariadb      | 11.4           |
| Microsoft SQL Server     | mssql        | 2022           |
| MySQL                    | mysql        | 8.4            |
| Oracle Database          | oracle       | 23.5           |
| PostgreSQL               | postgres     | 17             |
| Amazon Aurora PostgreSQL | postgres     | 16.8           |

* It is recommended to use 100GB storage for the sql db for up to 100000 users. The storage may increase if the number of users are increased.
  * You can spin up any databases from above in the AWS Aurora/RDS service or you can use a standalone db on a VM external to Aurora/RDS.
* **SSL Certificates**
  * Create an ACM certificate for the desired domain, which will be used by both the ECS tasks and the ALB HTTPS termination.
  * Please ensure you have a valid CA certificate for the ZTrust deployment.
* **AWS Secrets Manager**
  * The image pull secrets and SSL certificates can be stored in the AWS Secrets Manager in key/value pair format.
  * These will be referenced from the ECS task definition and will be used during the task execution
* **IAM Roles:**\
  The following IAM roles are required in order to deploy ZTrust SSO on ECS:
  * ecsTaskExecutionRole with:
    * AmazonECSTaskExecutionRolePolicy
    * secretsmanager:GetSecretValue on your secret ARNs
    * (logs permissions are included in the policy)
  * Optional ECS task role.
* **RabbitMQ – Version 3.12.2 (recommended)**

  ZTrust SSO uses RabbitMQ-based interactions with the ZTrust Authenticator app via the MQTT protocol. You should ensure that your RabbitMQ has the following:&#x20;

  * RabbitMQ version is 3.12.2
  * The TLS certificates are added to RabbitMQ, and MQTTS is configured.
  * A writable virtual host is configured with the right permissions of the topic exchanges for the RabbitMQ user.
  * For a production-grade RabbitMQ, it is recommended to go with 30 GB of disc space in the virtual host.
* **Build ECS Task Definition**\\

  Once the prerequisites are satisfied, you are ready to deploy the ZTrust SSO on your AWS ECS Cluster. Apps can be deployed on ECS by creating a task definition and then running the task/service on the ECS cluster.\\

  To create the Task definition,

  * Go to Amazon Elastic Container Service and click on Task Definition on the sidebar.

  * Click on 'Create Task Definition' in top right corner and select 'Create New Task Definition with JSON'

  * Use the following JSON:\\

    ```json
    {
      "family": "ztrust-sso",
      "containerDefinitions": [
        {
          "command": [
            "echo \"$TLS_CERT\" > /tmp/ztrust.crt && echo \"$TLS_KEY\" > /tmp/ztrust.key && exec /opt/ZTrust/bin/zt.sh start"
          ],
          "cpu": 0,
          "entryPoint": [
            "sh",
            "-c"
          ],
          "environment": [
            {
              "name": "ZT_DB_PASSWORD",
              "value": "<Your Database Password>"
            },
            {
              "name": "ZT_DB_USERNAME",
              "value": "<Your Database Username”"
            },
            {
              "name": "ZT_HOSTNAME",
              "value": "<the ZTrust SSO URL>"
            },
            {
              "name": "ZTRUST_ADMIN",
              "value": "<Set Your Admin Username>"
            },
            {
              "name": "ZTRUST_ADMIN_PASSWORD",
              "value": "<Set your Admin Password>"
            },
            {
              "name": "ZT_HTTPS_CERTIFICATE_FILE",
              "value": "<SSL Certificate File Location"
            },
            {
              "name": "ZT_PROXY",
              "value": "edge"
            },
            {
              "name": "ZT_DB",
              "value": "<Your Database Type>"
            },
            {
              "name": "PROXY_ADDRESS_FORWARDING",
              "value": "true"
            },
            {
              "name": "ZT_DB_URL",
              "value": "jdbc:postgresql://<IP or Hostname of DB>:<PortNumber>/<DB Name>"
            },
            {
              "name": "ZT_HTTPS_CERTIFICATE_KEY_FILE",
              "value": "<SSL Certificate Key File Location>"
            }
          ],
          "essential": true,
          "image": "registry.prodevans.com/ztrust/ztrust-sso:latest",
          "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
              "awslogs-group": "/ecs/ztrust-sso",
              "awslogs-create-group": "true",
              "awslogs-region": "us-east-1",
              "awslogs-stream-prefix": "ecs"
            }
          },
          "mountPoints": [],
          "name": "ZTrust-sso”, // <This is your task name>"
          "portMappings": [
            {
              "containerPort": 8443,
              "hostPort": 8443,
              "protocol": "tcp"
            }
          ],
          "repositoryCredentials": {
            "credentialsParameter": "arn:aws:secretsmanager:us-east-1:537xxxxxxxxx:secret:ZTrust-Registry-ox1BXS"
          },
          "secrets": [
            {
              "name": "TLS_CERT",
              "valueFrom": "arn:aws:secretsmanager:us-east-1:537xxxxxxxxx:secret:ztrust-cert-nU64Ks:tls.crt::"
            },
            {
              "name": "TLS_KEY",
              "valueFrom": "arn:aws:secretsmanager:us-east-1:537xxxxxxxxx:secret:ztrust-cert-nU64Ks:tls.key::"
            }
          ],
          "systemControls": [],
          "volumesFrom": []
        }
      ],
      "executionRoleArn": "arn:aws:iam::537xxxxxxxxx:role/ecsTaskExecutionRole",
      "networkMode": "awsvpc",
      "volumes": [],
      "placementConstraints": [],
      "requiresCompatibilities": [
        "FARGATE"
      ],
      "cpu": "1024",
      "memory": "3072"
    }
    ```

  * You can save this JSON and apply it with the below command with **AWS CLI** to create the task definition as well.\\

    ```bash
    aws ecs register-task-definition --cli-input-json file://ztrust-sso.json
    ```

  * Points to note on this task definition:
    * ECS doesn’t allow the secrets from AWS Secrets Manager to be mounted as read-only volumes. Hence the SSL secrets are attached as environment variables and then written into a file location, i.e., **/tmp/ztrust.crt** & **/tmp/ztrust.key**
    * These file locations are specified as the value for the `“ZT_HTTPS_CERTIFICATE_FILE"`  and  `"ZT_HTTPS_CERTIFICATE_KEY_FILE"`  environment variable.
    * In order to do this, the entry point was overridden, and the command was specified in the task definition file above.
    * Please replace the \<string> tags with actual values above or remove the tags wherever applicable.
* **Target Group**

  Create an HTTPS target group with health checks pointing to port 8443.

```sh
aws elbv2 create-target-group \
  --name ztrust-sso-tg \
  --protocol HTTPS \
  --port 8443 \
  --vpc-id vpc-xxxxxx \
  --target-type ip \
  --health-check-protocol HTTPS \
  --health-check-path / \
  --health-check-port 8443 \
  --matcher HttpCode=200-399

```

* **Application Load Balancer**
  * Create an ALB in public subnets.\\

    ```bash
    aws elbv2 create-load-balancer --name ztrust-alb --subnets subnet-publicA subnet-publicB --security-groups sg-alb
    ```
  * Attach an HTTPS listener with an ACM cert and forward to the target group.\\

    ```bash
    aws elbv2 create-listener --load-balancer-arn <load-balancer-arn> --protocol HTTPS --port 443 --certificates CertificateArn=<certificate-arn> --default-actions Type=forward,TargetGroupArn=<target-group-arn>
    ```
  * Add an HTTP listener to redirect HTTP -> HTTPS.

```bash
aws elbv2 create-listener --load-balancer-arn --protocol HTTP --port 80 --default-actions Type=redirect,RedirectConfig='{"Protocol":"HTTPS","Port":"443","StatusCode":"HTTP_301"}'
```

* **ECS Service**
  * Create an ECS service linked to the target group.
  * Launch type: FARGATE

```bash
aws ecs create-service \
  --cluster ZTrust-ECS \
  --service-name ztrust-sso-svc \
  --task-definition ztrust-sso \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration 'awsvpcConfiguration={subnets=["subnet-privA","subnet-privB"],securityGroups=["sg-task"],assignPublicIp="DISABLED"}' \
  --load-balancers 'targetGroupArn=<tg-arn>,containerName=ztrust-sso,containerPort=8443'
```

* **DNS**\
  Point Route53 record (auth.example.com) to the ALB DNS name. If you are using any external DNS for your domains, update the records there.

  \
  \\
