# Security Best Practices When Deploying MCP-Airflow-API: A Complete Guide

> Secure your MCP-Airflow-API deployment with essential best practices. Learn to implement token authentication, TLS termination, network restrictions, and robust secret management for production readiness.

- Repository: [JungJungIn/mcp-airflow-api](https://github.com/call518/mcp-airflow-api)
- Tags: best-practices
- Published: 2026-02-26

---

**Enable Bearer token authentication, use strong randomly generated secrets, terminate TLS at a reverse proxy, and restrict network access to trusted clients when deploying MCP-Airflow-API in production.**

MCP-Airflow-API is a Model Context Protocol (MCP) server that wraps the Apache Airflow REST API into natural-language tools. When exposing this server over HTTP, following security best practices when deploying MCP-Airflow-API becomes critical to protect your Airflow instance and prevent unauthorized access to DAGs and task metadata.

## Enable Authentication for Remote Deployments

The server supports **Bearer-token authentication** specifically for the `streamable-http` transport mode. In [`src/mcp_airflow_api/mcp_main.py`](https://github.com/call518/mcp-airflow-api/blob/main/src/mcp_airflow_api/mcp_main.py), the environment variable `REMOTE_AUTH_ENABLE` triggers the instantiation of a `StaticTokenVerifier` that validates incoming requests against a pre-shared secret【/cache/repos/github.com/call518/mcp-airflow-api/main/src/mcp_airflow_api/mcp_main.py#L38-L45】.

When enabled, the secret key is read from `REMOTE_SECRET_KEY` (or the `--secret-key` CLI flag) and used to build a static token map【/cache/repos/github.com/call518/mcp-airflow-api/main/src/mcp_airflow_api/mcp_main.py#L45-L53】.

### Configure Bearer Token Authentication

Set the following environment variables before starting the server:

```bash
export REMOTE_AUTH_ENABLE=true
export REMOTE_SECRET_KEY="$(openssl rand -base64 48)"

```

If `REMOTE_AUTH_ENABLE` is unset, the server defaults to `false` and emits a warning when `streamable-http` is used, making the fail-closed behavior explicit【/cache/repos/github.com/call518/mcp-airflow-api/main/src/mcp_airflow_api/mcp_main.py#L58-L62】.

## Secure Secret Management

### Generate Strong Random Secrets

The repository recommends a **32-character random secret** minimum for `REMOTE_SECRET_KEY`【/cache/repos/github.com/call518/mcp-airflow-api/main/README.md#L52-L66】. Use cryptographically secure random generation:

```bash

# Generate a 64-byte (512-bit) key

openssl rand -base64 48

```

### Externalize Secrets from Code

Never commit secrets to version control. The repository provides an `.env.example` file that you should copy to `.env` and populate with real values:

```bash
cp .env.example .env

# Edit .env with your secrets

echo ".env" >> .gitignore

```

For containerized deployments, inject secrets via Docker Secrets, Kubernetes Secrets, or a secrets manager like HashiCorp Vault.

## Transport Layer Security

### Terminate TLS at a Reverse Proxy

MCP-Airflow-API does **not** terminate TLS internally; it is designed to run behind a reverse proxy such as NGINX, Traefik, or Caddy. Configure your proxy to handle HTTPS and forward to the internal `FASTMCP_PORT` (default 8000).

Example NGINX configuration:

```nginx
server {
    listen 443 ssl;
    server_name mcp.example.com;

    ssl_certificate     /etc/ssl/certs/mcp.crt;
    ssl_certificate_key /etc/ssl/private/mcp.key;

    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

```

### Internal Port Configuration

The server listens on the port specified by `FASTMCP_PORT` (default 8000). Bind this to localhost only if running without a reverse proxy, though the reverse-proxy approach is strongly preferred for TLS.

## Network Isolation and Access Control

### Restrict Ingress to Trusted Networks

In production, limit exposure of the HTTP endpoint to trusted networks only. Deploy within an internal VPC, behind a VPN, or use firewall rules to restrict source IPs.

### Client IP Whitelisting

If your infrastructure supports it, configure your load balancer or reverse proxy to whitelist specific client IPs that are authorized to connect to the MCP server.

## Operational Security Practices

### Regular Secret Rotation

Rotate `REMOTE_SECRET_KEY` regularly (e.g., every 30 days). After updating the secret, restart the server so the new token map takes effect【/cache/repos/github.com/call518/mcp-airflow-api/main/src/mcp_airflow_api/mcp_main.py#L65-L73】.

Kubernetes example for zero-downtime rotation:

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: mcp-airflow-secret
type: Opaque
data:
  REMOTE_SECRET_KEY: <base64-encoded-new-key>

```

```bash
kubectl apply -f secret.yaml
kubectl rollout restart deployment/mcp-server

```

### Monitoring and Logging

Enable logging with `MCP_LOG_LEVEL=INFO` or higher to capture authentication failures. Monitor logs for `401 Unauthorized` responses, which indicate potential brute-force attempts or misconfigured clients【/cache/repos/github.com/call518/mcp-airflow-api/main/README.md#L11-L14】.

### Fail-Closed Defaults

The server implements fail-closed defaults: if `REMOTE_AUTH_ENABLE` is unset, authentication remains disabled but the server emits a clear warning when `streamable-http` transport is selected【/cache/repos/github.com/call518/mcp-airflow-api/main/src/mcp_airflow_api/mcp_main.py#L58-L62】. This makes the security posture explicit to operators.

## Deployment Examples

### Docker Compose with Authentication

```yaml
services:
  mcp-server:
    build:
      context: .
      dockerfile: Dockerfile.MCP-Server
    environment:
      - FASTMCP_TYPE=streamable-http
      - FASTMCP_PORT=8000
      - REMOTE_AUTH_ENABLE=true
      - REMOTE_SECRET_KEY=${REMOTE_SECRET_KEY}
    ports:
      - "127.0.0.1:8000:8000"

```

Store the secret in a `.env` file excluded from version control.

### Kubernetes Secret Rotation

As shown in the operational security section, use `kubectl rollout restart` after updating the Kubernetes Secret to apply new credentials without manual pod deletion.

### NGINX TLS Termination

Place the NGINX configuration shown earlier in [`/etc/nginx/conf.d/mcp-airflow-api.conf`](https://github.com/call518/mcp-airflow-api/blob/main//etc/nginx/conf.d/mcp-airflow-api.conf) and reload NGINX to encrypt all traffic to the MCP server.

## Summary

- **Enable Bearer token authentication** by setting `REMOTE_AUTH_ENABLE=true` and generating a strong `REMOTE_SECRET_KEY` when using `streamable-http` transport.
- **Use stdio transport** for local development to avoid network exposure entirely.
- **Terminate TLS at a reverse proxy** (NGINX, Traefik, Caddy) since the server does not handle HTTPS internally.
- **Externalize secrets** using environment variables, Docker Secrets, or Kubernetes Secrets—never commit credentials to version control.
- **Rotate secrets regularly** (every 30 days) and restart the server to load new token maps.
- **Monitor logs** for `401 Unauthorized` errors to detect unauthorized access attempts.
- **Restrict network access** to trusted VPCs, VPNs, or whitelisted client IPs.

## Frequently Asked Questions

### Do I need authentication if I run MCP-Airflow-API locally?

No. When using the default **stdio** transport mode, the server runs as a child process of your local client with no network exposure, making authentication unnecessary. Only enable `REMOTE_AUTH_ENABLE` when using `streamable-http` transport for remote access【/cache/repos/github.com/call518/mcp-airflow-api/main/src/mcp_airflow_api/mcp_main.py#L75-L81】.

### How long should my REMOTE_SECRET_KEY be?

The repository recommends a minimum of **32 characters**, but you should generate a cryptographically secure random string of at least 48 bytes (64 base64 characters) using `openssl rand -base64 48`. Store this in an environment variable or secrets manager, never in your codebase【/cache/repos/github.com/call518/mcp-airflow-api/main/README.md#L52-L66】.

### Can MCP-Airflow-API handle HTTPS termination internally?

No. The server is designed to run behind a reverse proxy such as NGINX, Traefik, or Caddy. Configure your proxy to handle TLS termination and forward decrypted traffic to the server's `FASTMCP_PORT` (default 8000). This architecture keeps the Python server simple while ensuring encrypted transport.

### What happens if I forget to enable authentication on a public deployment?

If `REMOTE_AUTH_ENABLE` is unset or `false` while using `streamable-http`, the server will start without authentication but emit a clear warning in the logs. However, this leaves your Airflow instance exposed to anyone who can reach the endpoint. Always verify `REMOTE_AUTH_ENABLE=true` is set before exposing the server to untrusted networks【/cache/repos/github.com/call518/mcp-airflow-api/main/src/mcp_airflow_api/mcp_main.py#L58-L62】.