Post

Build a Pritunl Slack Slash Commands with a Serverless Backend

Introduction

For self-service automation, Slack is one of the leading platforms that it can provide. As DevOps, which also considers company administrators, we ensure team members, such as Developers or QA, are presented with their technical needs without always relying on us.

In this post, we will build and deploy a Slack Slash Command for Pritunl Enterprise VPN users so they can generate their VPN Profile and Keys to access the organization’s resources.

Quick Demo

Pritunl Slack Profile Key Pritunl Slack Profile Key Figure 1: Pritunl Slack Slash Commands Application

Slack App Setup

Create a new Slack application

Go to Slack API -> Your Apps -> Create New App.

Create an app Steps

From Create an app pop-up window -> choose From an app manifest.

Step 1: Pick a workspace to develop your app

Pick a Workspace to develop your app in. Then proceed to next step.

Step 2: Enter app manifest below

The App manifest contains basic info, scopes, settings, and features.

Choose the YAML option, then copy and paste the code provided for you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
display_information:
  name: Pritunl
  description: Pritunl
  background_color: "#737373"
features:
  bot_user:
    display_name: Pritunl
    always_online: false
  slash_commands:
    - command: /pritunl
      description: Pritunl
      should_escape: false

      ## Replace the URL with the actual endpoint later after Serverless backend deployment.
      url: https://url-id.lambda-url.us-east-1.on.aws/

oauth_config:
  scopes:
    bot:
      - commands
      - users:read
      - users:read.email
settings:
  org_deploy_enabled: false
  socket_mode_enabled: false
  token_rotation_enabled: false

Once fill-in and without syntax or definition errors, proceed to next step.

Step 3: Review summary & create your app

Based on the YAML manifest from our previous step, inspect the OAuth and Features that suit our requirements, and finalize the app creation steps by clicking the Create button.

Improve App Visual Identity (Optional)

For our application to have a unique visual identity from the rest of our apps, we can change the app icon.

From the Pritunl app, go to -> Settings -> Basic Information -> App Credentials -> Display Information.

Download the Pritunl Icon and, upload it to the App icon & Preview

Prepare the Slack Credentials

We only need the Signing Secret and Bot Token. Then steps on how to obtain these.

Getting the Signing Secret

From the Pritunl app, go to -> Settings -> Basic Information -> App Credentials.

Then, click the Show button in the Signing Secret field to suppress the information and copy it for our Serverless backend deployment.

Getting the Bot Token

From the Pritunl app, go to -> Settings -> Basic Information -> Install App.

Then on the Install App to Your Team page, click the Install to Workspace.

Review the app requesting permission to access our workspace, then click Allow.

Once installed in our workspace, the field Bot User OAuth Token shows this will be our Bot Token, which we copy for our Serverless backend deployment.

Deploy the Serverless Backend

Prepare the Pritunl Credentials

Before proceeding to Serverless backend deployment, also prepare the Pritunl credentials.

From the Pritunl Dashboard (Base URL: https://vpn.domain.tld/), go to -> Administrators -> Select an Administrator -> then tick the Enable Token Authentication.

Copy the Pritunl API Token and API Secret.

Tooling Requirements

Install the AWS SAM CLI

Make sure you have the latest AWS SAM CLI installed in your system.

Serverless Backend Deployment

We will use the GitHub repository Pritunl Slack App Slash Commands application as our backend for the Slack application.

Deploy to AWS Lambda

Pritunl Slack Profile Key Pritunl Slack Profile Key Figure 2: Pritunl Slack Slash Commands Serverless Backend Deployment

Recorded with asciinema asciinema

Clone the Serverless Backend

1
2
git clone https://github.com/nathanielvarona/pritunl-slack-app.git
cd pritunl-slack-app

Deploy using AWS SAM

Make sure you have AWS SAM CLI installed.

Load the Credentials as Environment Variables

1
2
3
4
5
export PRITUNL_BASE_URL="https://vpn.domain.tld/"
export PRITUNL_API_SECRET="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
export PRITUNL_API_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
export SLACK_SIGNING_SECRET="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
export SLACK_BOT_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

Build the Serverless Application

The flag --use-container only works if Docker is installed. This method prevents failing builds and packaging issues and promotes the highest runtime compatibility on the serverless environment once deployed.

1
sam build --use-container --no-cached
Show Stdout
  Starting Build inside a container
  Building codeuri: .../pritunl-slack-app/pritunl_slack_app/function runtime: python3.10 metadata: {} architecture: x86_64 functions: PritunlSlackFunction
  
  Fetching public.ecr.aws/sam/build-python3.10:latest-x86_64 Docker container image......
  Mounting .../pritunl-slack-app/pritunl_slack_app/function as /tmp/samcli/source:ro,delegated, inside runtime container
  Running PythonPipBuilder:ResolveDependencies
  Running PythonPipBuilder:CopySource
  
  Build Succeeded
  
  Built Artifacts  : .aws-sam/build
  Built Template   : .aws-sam/build/template.yaml
  
  Commands you can use next
  =========================
  [*] Validate SAM template: sam validate
  [*] Invoke Function: sam local invoke
  [*] Test Function in the Cloud: sam sync --stack-name  --watch
  [*] Deploy: sam deploy --guided
  

Lets deploy the application

The flag --no-confirm-changeset will automatically deploy the application without reviews.

1
2
3
4
5
6
sam deploy --no-confirm-changeset --parameter-overrides "\
  PritunlBaseUrl=${PRITUNL_BASE_URL} \
  PritunlApiSecret=${PRITUNL_API_SECRET} \
  PritunlApiToken=${PRITUNL_API_TOKEN} \
  SlackSigningSecret=${SLACK_SIGNING_SECRET} \
  SlackBotToken=${SLACK_BOT_TOKEN}"
Show Stdout
      Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-XXXXXXXXXXXXX
      A different default S3 bucket can be set in samconfig.toml
      Or by specifying --s3-bucket explicitly.
    Uploading to pritunl-slack-app/285144b7022df545f09fb472228be5a9  1073 / 1073  (100.00%)
    Uploading to pritunl-slack-app/76f170eb0196cbb12a272dfde42894c7  899 / 899  (100.00%)
    Uploading to pritunl-slack-app/1ba998885207ab33f89be0e16954258a  1104939 / 1104939  (100.00%)
  
    Deploying with following values
    ===============================
    Stack name                   : pritunl-slack-app
    Region                       : us-east-1
    Confirm changeset            : False
    Disable rollback             : False
    Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-XXXXXXXXXXXXX
    Capabilities                 : ["CAPABILITY_IAM"]
    Parameter overrides          : {"PritunlBaseUrl": "*****", "PritunlApiSecret": "*****", "PritunlApiToken": "*****", "SlackSigningSecret": "*****", "SlackBotToken": "*****"}
    Signing Profiles             : {}
  
  Initiating deployment
  =====================
  
    Uploading to pritunl-slack-app/c1d1651005d8166da9b4e6fd66f52f6b.template  5546 / 5546  (100.00%)
  
  
  Waiting for changeset to be created..
  
  CloudFormation stack changeset
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Operation                                    LogicalResourceId                            ResourceType                                 Replacement
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  + Add                                        AWSPritunlBaseUrl                            AWS::SecretsManager::Secret                  N/A
  + Add                                        AWSSecretPritunlApiSecret                    AWS::SecretsManager::Secret                  N/A
  + Add                                        AWSSecretPritunlApiToken                     AWS::SecretsManager::Secret                  N/A
  + Add                                        AWSSecretSlackBotToken                       AWS::SecretsManager::Secret                  N/A
  + Add                                        AWSSecretSlackSigningSecret                  AWS::SecretsManager::Secret                  N/A
  + Add                                        PritunlSlackFunctionRole                     AWS::IAM::Role                               N/A
  + Add                                        PritunlSlackFunctionUrl                      AWS::Lambda::Url                             N/A
  + Add                                        PritunlSlackFunction                         AWS::Lambda::Function                        N/A
  + Add                                        PritunlSlackUrlFunctionPermissions           AWS::Lambda::Permission                      N/A
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  
  
  Changeset created successfully. arn:aws:cloudformation:us-east-1:############:changeSet/samcli-deploy1682663457/a848f39c-418b-47d9-8227-f6aa79ae2fa7
  
  
  2023-04-28 14:31:07 - Waiting for stack create/update to complete
  
  CloudFormation events from stack operations (refresh every 5.0 seconds)
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  ResourceStatus                               ResourceType                                 LogicalResourceId                            ResourceStatusReason
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  CREATE_IN_PROGRESS                           AWS::CloudFormation::Stack                   pritunl-slack-app                            User Initiated
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretSlackBotToken                       -
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretSlackSigningSecret                  -
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSPritunlBaseUrl                            -
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretPritunlApiToken                     -
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretPritunlApiSecret                    -
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretSlackSigningSecret                  Resource creation Initiated
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretSlackBotToken                       Resource creation Initiated
  CREATE_COMPLETE                              AWS::SecretsManager::Secret                  AWSSecretSlackSigningSecret                  -
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSPritunlBaseUrl                            Resource creation Initiated
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretPritunlApiSecret                    Resource creation Initiated
  CREATE_COMPLETE                              AWS::SecretsManager::Secret                  AWSSecretSlackBotToken                       -
  CREATE_IN_PROGRESS                           AWS::SecretsManager::Secret                  AWSSecretPritunlApiToken                     Resource creation Initiated
  CREATE_COMPLETE                              AWS::SecretsManager::Secret                  AWSSecretPritunlApiSecret                    -
  CREATE_COMPLETE                              AWS::SecretsManager::Secret                  AWSPritunlBaseUrl                            -
  CREATE_COMPLETE                              AWS::SecretsManager::Secret                  AWSSecretPritunlApiToken                     -
  CREATE_IN_PROGRESS                           AWS::IAM::Role                               PritunlSlackFunctionRole                     -
  CREATE_IN_PROGRESS                           AWS::IAM::Role                               PritunlSlackFunctionRole                     Resource creation Initiated
  CREATE_COMPLETE                              AWS::IAM::Role                               PritunlSlackFunctionRole                     -
  CREATE_IN_PROGRESS                           AWS::Lambda::Function                        PritunlSlackFunction                         -
  CREATE_IN_PROGRESS                           AWS::Lambda::Function                        PritunlSlackFunction                         Resource creation Initiated
  CREATE_COMPLETE                              AWS::Lambda::Function                        PritunlSlackFunction                         -
  CREATE_IN_PROGRESS                           AWS::Lambda::Permission                      PritunlSlackUrlFunctionPermissions           -
  CREATE_IN_PROGRESS                           AWS::Lambda::Url                             PritunlSlackFunctionUrl                      -
  CREATE_IN_PROGRESS                           AWS::Lambda::Permission                      PritunlSlackUrlFunctionPermissions           Resource creation Initiated
  CREATE_IN_PROGRESS                           AWS::Lambda::Url                             PritunlSlackFunctionUrl                      Resource creation Initiated
  CREATE_COMPLETE                              AWS::Lambda::Url                             PritunlSlackFunctionUrl                      -
  CREATE_COMPLETE                              AWS::Lambda::Permission                      PritunlSlackUrlFunctionPermissions           -
  CREATE_COMPLETE                              AWS::CloudFormation::Stack                   pritunl-slack-app                            -
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  
  CloudFormation outputs from deployed stack
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Outputs
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Key                 PritunlSlackFunction
  Description         Pritunl Slack App Lambda Function ARN
  Value               arn:aws:lambda:us-east-1:############:function:pritunl-slack-app-PritunlSlackFunction-hae24jz29SIN
  
  Key                 PritunlSlackFunctionUrl
  Description         Pritunl Slack App Lambda Function URL
  Value               https://5lxvqp66eussnx4egwg5hz7dme0vbend.lambda-url.us-east-1.on.aws/
  
  Key                 PritunlSlackFunctionIamRole
  Description         Implicit IAM Role created for Pritunl Slack App function
  Value               arn:aws:iam::############:role/pritunl-slack-app-PritunlSlackFunctionRole-XGLNKCQZS3H5
  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  
  
  Successfully created/updated stack - pritunl-slack-app in us-east-1
  

Modify our Slack Slash Commands configuration.

Copy the value of PritunlSlackFunctionUrl from our sam deploy outputs, such as the URL https://5lxvqp66eussnx4egwg5hz7dme0vbend.lambda-url.us-east-1.on.aws/. Then modify our Slack Slash Commands Manifest configuration.

From the Pritunl app, go to -> Features -> App Manifest -> YAML.

1
2
3
features:
  slash_commands:
    - url: https://5lxvqp66eussnx4egwg5hz7dme0vbend.lambda-url.us-east-1.on.aws/

Modify and Save Changes.

And we are done! We now have a working Pritunl Slack Application, as shown in the Quick Demo.

Other Methods of Deployment

Lambda Functions Using Docker Image as a Package Type

Only requires few changes for our SAM template.yaml to work with Docker Image

Patch the SAM Template

Apply the patch file template.yaml.docker-image.patch for our template.yaml using git.

1
git apply ./template.yaml.docker-image.patch

Build using SAM with extra Parameters

Docker requires extra build arguments to pass the AWS Credentials to download the AWS Parameter and Secrets Lambda extension at the build stage.

1
2
3
sam build --use-container --no-cached --parameter-overrides "\
  AwsAccessKeyId=${AWS_ACCESS_KEY_ID} \
  AwsSecretAccessKey=${AWS_SECRET_ACCESS_KEY}"

Then Deploy

The same way as how the Zip Package Type deploys.

1
2
3
4
5
6
sam deploy --no-confirm-changeset  --parameter-overrides "\
  PritunlBaseUrl=${PRITUNL_BASE_URL} \
  PritunlApiSecret=${PRITUNL_API_SECRET} \
  PritunlApiToken=${PRITUNL_API_TOKEN} \
  SlackSigningSecret=${SLACK_SIGNING_SECRET} \
  SlackBotToken=${SLACK_BOT_TOKEN}"

Kubernetes Cluster with Helm Package Manager

If you are in Kubernetes Cluster and highly reliant on Helm Package Manager, please consider checking the project I also created, the Pritunl Slack App Helm Chart.

This post is licensed under CC BY 4.0 by the author.