Continuous Delivery and GitOps

Bowtie’s access configuration, including policies, resources, resource groups, DNS, and sites, can be managed as code with the Bowtie Terraform provider and applied through your existing delivery pipeline. This page shows a GitOps workflow that plans changes on a pull request and applies them on merge, along with a pre-deploy policy check that verifies access decisions before they ship.

Managing access as code

Keep your bowtie_* Terraform in version control alongside the rest of your infrastructure. A typical GitOps flow runs terraform plan on every pull request so reviewers see the proposed change, and runs terraform apply once the change merges to your main branch. Store Terraform state in a shared backend so the pipeline and your team share one source of truth.

Provider setup, authentication, the terraform import workflow, and an apply-ordering pattern that waits for a Controller’s API to become reachable are covered under Bowtie Terraform Provider. The pipelines below assume the provider is configured from the BOWTIE_HOST, BOWTIE_USERNAME, and BOWTIE_PASSWORD environment variables, supplied as pipeline secrets.

GitHub Actions

This workflow validates and plans on a pull request, and applies on a push to main.

name: bowtie
on:
  pull_request:
  push:
    branches: [main]
jobs:
  terraform:
    runs-on: ubuntu-latest
    env:
      BOWTIE_HOST: ${{ secrets.BOWTIE_HOST }}
      BOWTIE_USERNAME: ${{ secrets.BOWTIE_USERNAME }}
      BOWTIE_PASSWORD: ${{ secrets.BOWTIE_PASSWORD }}
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform fmt -check
      - run: terraform plan -input=false
      - if: github.ref == 'refs/heads/main'
        run: terraform apply -auto-approve -input=false

GitLab CI

The same flow expressed as a GitLab pipeline, with the secrets set as masked CI/CD variables.

default:
  image: hashicorp/terraform:latest
stages: [validate, deploy]
plan:
  stage: validate
  script:
    - terraform init
    - terraform fmt -check
    - terraform plan -input=false
apply:
  stage: deploy
  script:
    - terraform init
    - terraform apply -auto-approve -input=false
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Pre-deploy policy checks

A Controller can return the access decision for a hypothetical connection before you ship a change. The POST /-net/api/v0/policy/explain endpoint takes a source, a destination, a protocol, and an optional port, and returns the verdict that the enforcement engine would apply. Because it calls the same decision logic that enforces traffic in the kernel, the answer is the real policy outcome rather than a separate simulation.

This makes it a pre-deploy policy gate: assert in your pipeline that intended access works and that unintended access is denied, and fail the job when a verdict does not match. Authenticate as described under API Keys, then call the endpoint:

# Does alice-laptop reach the finance app over HTTPS?
curl -sS -X POST "https://$BOWTIE_HOST/-net/api/v0/policy/explain" \
  -H "Content-Type: application/json" \
  -H "Authorization: $BOWTIE_API_KEY" \
  -d '{
        "party_one": "alice-laptop",
        "party_two": "finance.internal.example.com",
        "protocol": "TCP",
        "party_two_port": 443
      }' \
  | jq '.responses[].verdict'

The response carries one verdict per resolved address pair. Read the verdict and fail the pipeline when it does not match the decision you expect, so a policy change that would open or close the wrong path is caught before it merges. The source and destination accept a device name, an IP address, or a DNS name, so you can express checks in the same terms your policies use.

Reference status

The policy evaluator is currently in beta. Its request and response shapes may change between releases, so pin your assertions to the fields you read and verify them against your Controller’s responses.