> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/ansible/awx/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> Security features, credential encryption, authentication methods, HTTPS, and RBAC in AWX

## Overview

AWX implements multiple layers of security including credential encryption, role-based access control, authentication providers, and secure communication. Understanding these features is essential for maintaining a secure automation platform.

## Credential Encryption

AWX encrypts all sensitive credential data before storing it in the database.

### Encryption Method

AWX uses **AES-256 in CBC mode** with the following characteristics:

* 256-bit encryption key
* PKCS7 padding
* HMAC using SHA-256 for authentication
* Unique initialization vector (IV) per encrypted value

Encrypted fields include:

* Passwords and passphrases
* SSH private keys
* API tokens and secret keys
* Cloud service credentials
* Custom credential type secret fields
* External service authentication tokens

### Secret Key Management

The `SECRET_KEY` is the master encryption key for all sensitive data.

**Location:**

* Traditional: `/etc/tower/SECRET_KEY`
* Kubernetes: Secret named `awx-secret-key`

<Danger>
  Never commit the SECRET\_KEY to version control or expose it in logs. Loss of the SECRET\_KEY makes all encrypted credentials permanently unrecoverable.
</Danger>

#### Rotating the Secret Key

To rotate the SECRET\_KEY:

<Steps>
  <Step title="Generate new key">
    ```bash theme={null}
    openssl rand -base64 32 > NEW_SECRET_KEY
    ```
  </Step>

  <Step title="Decrypt with old key">
    Use `awx-manage` to decrypt credentials with the current key:

    ```bash theme={null}
    kubectl exec -it -n awx deployment/awx-task -c awx-task -- \
      awx-manage shell_plus
    ```

    ```python theme={null}
    from awx.main.utils import decrypt_field, encrypt_field
    from awx.main.models import Credential

    # Document current credentials before rotation
    for cred in Credential.objects.all():
        print(f"Credential {cred.id}: {cred.name}")
    ```
  </Step>

  <Step title="Update SECRET_KEY">
    **Kubernetes:**

    ```bash theme={null}
    kubectl create secret generic awx-secret-key \
      --from-file=secret_key=./NEW_SECRET_KEY \
      -n awx --dry-run=client -o yaml | kubectl apply -f -
    kubectl rollout restart deployment/awx-web -n awx
    kubectl rollout restart deployment/awx-task -n awx
    ```

    **Traditional:**

    ```bash theme={null}
    cp NEW_SECRET_KEY /etc/tower/SECRET_KEY
    chown awx:awx /etc/tower/SECRET_KEY
    chmod 600 /etc/tower/SECRET_KEY
    systemctl restart awx
    ```
  </Step>

  <Step title="Re-encrypt credentials">
    All credentials must be updated with the new key. This requires re-entering sensitive values through the UI or API.
  </Step>
</Steps>

<Warning>
  Secret key rotation requires re-creating all credentials. Plan this during a maintenance window and have credential values ready to re-enter.
</Warning>

### Extracting Encrypted Values

If you need to extract credentials for migration or debugging:

```python theme={null}
# Access AWX shell
kubectl exec -it -n awx deployment/awx-task -c awx-task -- awx-manage shell_plus

# Decrypt a credential field
from awx.main.utils import decrypt_field
from awx.main.models import Credential

cred = Credential.objects.get(name="my-credential")
decrypted_password = decrypt_field(cred, "password")
print(decrypted_password)

# Decrypt a setting
from awx.conf.models import Setting
setting = Setting.objects.get(key='SOCIAL_AUTH_GITHUB_SECRET')
decrypted_value = decrypt_field(setting, 'value')
```

<Warning>
  Only extract credentials in secure environments. Never log or expose decrypted values.
</Warning>

## External Secret Management

AWX can retrieve secrets from external secret management systems instead of storing them encrypted in the database.

### Supported Systems

* **HashiCorp Vault KV** — Key/Value secret engine
* **HashiCorp Vault SSH** — SSH certificate signing
* **Microsoft Azure Key Vault**
* **CyberArk Conjur**
* **Thycotic Secret Server**

### HashiCorp Vault Integration

<Tabs>
  <Tab title="Setup">
    Create a HashiCorp Vault credential:

    ```bash theme={null}
    curl -X POST https://awx.example.org/api/v2/credentials/ \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "HashiCorp Vault",
        "credential_type": 15,
        "inputs": {
          "url": "https://vault.example.org",
          "token": "hvs.XXXXXX",
          "api_version": "v2"
        }
      }'
    ```
  </Tab>

  <Tab title="Link to credential">
    Link a credential field to Vault:

    ```bash theme={null}
    curl -X POST https://awx.example.org/api/v2/credentials/1/input_sources/ \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{
        "source_credential": 2,
        "input_field_name": "password",
        "metadata": {
          "secret_path": "/kv/data/my-secret",
          "secret_key": "password"
        }
      }'
    ```
  </Tab>
</Tabs>

<Info>
  External secrets are fetched on-demand when jobs run. They are never stored in the AWX database, providing enhanced security for sensitive credentials.
</Info>

### Custom Credential Plugins

You can write custom credential plugins to integrate with additional secret management systems:

```python theme={null}
# Example custom credential plugin
def my_secret_backend(**kwargs):
    url = kwargs.get('url')
    token = kwargs.get('token')
    secret_path = kwargs.get('secret_path')
    
    # Fetch secret from your system
    response = requests.get(
        f"{url}/{secret_path}",
        headers={"Authorization": f"Bearer {token}"}
    )
    return response.json()['secret']
```

Register the plugin using setuptools entrypoints. See the [AWX custom credential plugin example](https://github.com/ansible/awx-custom-credential-plugin-example).

## Authentication Methods

AWX supports multiple authentication backends for user login.

### Local Authentication

Default username/password authentication using the AWX database.

**Enable local authentication:**

```bash theme={null}
kubectl exec -it -n awx deployment/awx-task -c awx-task -- \
  awx-manage enable_local_authentication
```

**Create a local user:**

```bash theme={null}
curl -X POST https://awx.example.org/api/v2/users/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "analyst",
    "password": "secure_password",
    "email": "analyst@example.org",
    "is_superuser": false
  }'
```

### LDAP Authentication

Configure LDAP for centralized user management:

```python theme={null}
# /etc/tower/conf.d/ldap.py
AUTH_LDAP_SERVER_URI = 'ldap://ldap.example.org'
AUTH_LDAP_BIND_DN = 'cn=awx,ou=services,dc=example,dc=org'
AUTH_LDAP_BIND_PASSWORD = 'ldap_password'
AUTH_LDAP_USER_SEARCH = LDAPSearch(
    'ou=users,dc=example,dc=org',
    ldap.SCOPE_SUBTREE,
    '(uid=%(user)s)'
)
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
    'ou=groups,dc=example,dc=org',
    ldap.SCOPE_SUBTREE,
    '(objectClass=groupOfNames)'
)
AUTH_LDAP_USER_ATTR_MAP = {
    'first_name': 'givenName',
    'last_name': 'sn',
    'email': 'mail'
}
```

Apply via the API:

```bash theme={null}
curl -X PATCH https://awx.example.org/api/v2/settings/ldap/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "AUTH_LDAP_SERVER_URI": "ldap://ldap.example.org",
    "AUTH_LDAP_BIND_DN": "cn=awx,ou=services,dc=example,dc=org",
    "AUTH_LDAP_BIND_PASSWORD": "ldap_password"
  }'
```

### SAML Authentication

Configure SAML 2.0 for single sign-on:

```bash theme={null}
curl -X PATCH https://awx.example.org/api/v2/settings/saml/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "SOCIAL_AUTH_SAML_ENABLED_IDPS": {
      "myidp": {
        "entity_id": "https://idp.example.org",
        "url": "https://idp.example.org/sso",
        "x509cert": "MIIDXTCCAkWgAwIBAgIJ..."
      }
    },
    "SOCIAL_AUTH_SAML_SP_ENTITY_ID": "https://awx.example.org",
    "SOCIAL_AUTH_SAML_SP_PUBLIC_CERT": "MIIDXTCCAkWgAwIBAgIJ...",
    "SOCIAL_AUTH_SAML_SP_PRIVATE_KEY": "-----BEGIN PRIVATE KEY-----..."
  }'
```

### OAuth 2.0 / OpenID Connect

Supported providers:

* GitHub
* Google OAuth2
* Azure AD
* Generic OIDC

**GitHub example:**

```bash theme={null}
curl -X PATCH https://awx.example.org/api/v2/settings/github/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "SOCIAL_AUTH_GITHUB_KEY": "github_oauth_client_id",
    "SOCIAL_AUTH_GITHUB_SECRET": "github_oauth_client_secret",
    "SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP": {
      "Default": {
        "users": true
      },
      "Engineering": {
        "admins": ["my-github-org"]
      }
    }
  }'
```

## Role-Based Access Control (RBAC)

AWX uses django-ansible-base for role-based access control. RBAC determines what users can view, create, edit, or delete.

### Role Types

<CardGroup cols={2}>
  <Card title="System Roles" icon="shield">
    * **System Administrator** — Full control over AWX
    * **System Auditor** — Read-only access to all objects
  </Card>

  <Card title="Object Roles" icon="key">
    * **Admin** — Full control over an object and its children
    * **Execute** — Can launch jobs or workflows
    * **Update** — Can modify object configuration
    * **Use** — Can use but not modify (credentials, inventories)
    * **Read** — Read-only access to an object
  </Card>
</CardGroup>

### Assigning Roles

**Via API:**

```bash theme={null}
# Grant user execute permission on a job template
curl -X POST https://awx.example.org/api/v2/job_templates/5/access_list/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "user": 12,
    "role_definition": "JobTemplate Execute"
  }'

# Grant team admin permission on an organization
curl -X POST https://awx.example.org/api/v2/organizations/3/access_list/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "team": 8,
    "role_definition": "Organization Admin"
  }'
```

**Via awx CLI:**

```bash theme={null}
awx role grant --user analyst --job-template "Deploy App" --role execute
awx role grant --team developers --organization "Engineering" --role admin
```

### RBAC Hierarchy

Permissions inherit down the object hierarchy:

```
Organization (Admin)
  ├── Inventories (Inherited Admin)
  │   └── Hosts (Inherited Admin)
  ├── Projects (Inherited Admin)
  └── Job Templates (Inherited Admin)
```

<Tip>
  Grant roles at the highest appropriate level to simplify permission management. Organization admins automatically have admin rights to all child objects.
</Tip>

### Checking User Permissions

```bash theme={null}
# List all roles for a user
curl -X GET https://awx.example.org/api/v2/users/12/roles/ \
  -H "Authorization: Bearer <token>"

# Check if user can execute a job template
curl -X GET https://awx.example.org/api/v2/job_templates/5/ \
  -H "Authorization: Bearer <token>" \
  | jq '.summary_fields.user_capabilities.start'
```

## HTTPS and TLS Configuration

### Kubernetes with Ingress

Configure TLS using Kubernetes Ingress:

```yaml awx-ingress.yaml theme={null}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: awx-ingress
  namespace: awx
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - awx.example.org
    secretName: awx-tls-cert
  rules:
  - host: awx.example.org
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: awx-service
            port:
              number: 80
```

### Traditional Deployment with Nginx

Configure Nginx as a reverse proxy with TLS:

```nginx theme={null}
server {
    listen 443 ssl http2;
    server_name awx.example.org;

    ssl_certificate /etc/ssl/certs/awx.crt;
    ssl_certificate_key /etc/ssl/private/awx.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://localhost:8052;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /websocket {
        proxy_pass http://localhost:8052/websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

server {
    listen 80;
    server_name awx.example.org;
    return 301 https://$server_name$request_uri;
}
```

### Certificate Management

**Using cert-manager (Kubernetes):**

```yaml theme={null}
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: awx-tls
  namespace: awx
spec:
  secretName: awx-tls-cert
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - awx.example.org
```

**Manual certificate:**

```bash theme={null}
# Generate self-signed certificate (development only)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout awx.key -out awx.crt \
  -subj "/CN=awx.example.org"

# Create Kubernetes secret
kubectl create secret tls awx-tls-cert \
  --cert=awx.crt --key=awx.key -n awx
```

<Warning>
  Never use self-signed certificates in production. Use certificates from a trusted CA or Let's Encrypt.
</Warning>

## Network Security

### Firewall Configuration

Required ports:

| Port | Protocol | Purpose |
| - | - | - |
| 443 | TCP | HTTPS web interface |
| 80 | TCP | HTTP (redirect to HTTPS) |
| 27199 | TCP | Receptor mesh (between AWX instances) |
| 5432 | TCP | PostgreSQL (internal only) |

**Kubernetes network policies:**

```yaml theme={null}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: awx-network-policy
  namespace: awx
spec:
  podSelector:
    matchLabels:
      app: awx
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 8052
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
    ports:
    - protocol: TCP
      port: 5432
```

### Database Connection Security

Configure PostgreSQL to require SSL:

```python theme={null}
# /etc/tower/conf.d/postgres.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'awx',
        'USER': 'awx',
        'PASSWORD': os.environ.get('AWX_DB_PASSWORD'),
        'HOST': 'postgres.example.org',
        'PORT': '5432',
        'OPTIONS': {
            'sslmode': 'require',
            'sslrootcert': '/etc/ssl/certs/ca-bundle.crt'
        }
    }
}
```

## Security Best Practices

<AccordionGroup>
  <Accordion title="Principle of least privilege">
    * Grant users only the minimum permissions needed
    * Use team-based permissions instead of individual user permissions
    * Regularly audit user roles and remove unnecessary access
    * Avoid granting System Administrator role unless absolutely necessary
  </Accordion>

  <Accordion title="Credential management">
    * Use external secret management (Vault, Azure Key Vault) for sensitive credentials
    * Rotate credentials regularly (every 90 days minimum)
    * Never hardcode credentials in playbooks or job templates
    * Use separate credentials for each environment (dev, staging, production)
    * Enable credential input on launch only when necessary
  </Accordion>

  <Accordion title="Authentication security">
    * Enforce multi-factor authentication (MFA) through your identity provider
    * Use SSO (SAML/OAuth) instead of local passwords when possible
    * Set strong password policies for local accounts
    * Implement session timeouts: `SESSION_COOKIE_AGE = 3600` (1 hour)
    * Disable unused authentication backends
  </Accordion>

  <Accordion title="Network security">
    * Always use HTTPS/TLS for web interface access
    * Restrict AWX access to internal networks or VPN
    * Use firewall rules to limit database access to AWX hosts only
    * Enable network policies in Kubernetes
    * Use private container registries for execution environments
  </Accordion>

  <Accordion title="Audit and monitoring">
    * Enable activity stream logging for all object changes
    * Export logs to a centralized logging system (Splunk, ELK, etc.)
    * Monitor failed authentication attempts
    * Alert on privilege escalation (user becomes admin)
    * Review audit logs regularly for suspicious activity
  </Accordion>

  <Accordion title="System hardening">
    * Keep AWX updated to the latest stable version
    * Apply security patches promptly
    * Minimize installed packages in execution environments
    * Run AWX containers as non-root users
    * Enable SELinux or AppArmor where applicable
    * Regular vulnerability scanning of container images
  </Accordion>
</AccordionGroup>

## Security Auditing

### Activity Stream

All object changes are logged in the activity stream:

```bash theme={null}
# View all activity
curl -X GET https://awx.example.org/api/v2/activity_stream/ \
  -H "Authorization: Bearer <token>"

# View changes to a specific credential
curl -X GET https://awx.example.org/api/v2/credentials/5/activity_stream/ \
  -H "Authorization: Bearer <token>"

# Filter by action type
curl -X GET "https://awx.example.org/api/v2/activity_stream/?operation=delete" \
  -H "Authorization: Bearer <token>"
```

### Export Audit Logs

```bash theme={null}
# Export last 30 days of activity
curl -X GET "https://awx.example.org/api/v2/activity_stream/?timestamp__gte=$(date -d '30 days ago' -Iseconds)" \
  -H "Authorization: Bearer <token>" \
  | jq '.results[] | {timestamp, operation, object_type, changes}' \
  > audit_log_$(date +%Y%m%d).json
```

### Compliance Reporting

Generate compliance reports:

```python theme={null}
# Access AWX shell
kubectl exec -it -n awx deployment/awx-task -c awx-task -- awx-manage shell_plus

# Report on users with admin access
from django.contrib.auth.models import User
from awx.main.models import Role

admins = User.objects.filter(
    roles__role_field='admin_role'
).distinct()

for user in admins:
    print(f"{user.username}: {user.email}")

# Report on credentials not used in 90 days
from datetime import timedelta
from django.utils import timezone
from awx.main.models import Credential

cutoff = timezone.now() - timedelta(days=90)
unused_creds = Credential.objects.filter(
    jobtemplates__isnull=True,
    created__lt=cutoff
)

for cred in unused_creds:
    print(f"Unused: {cred.name} (created {cred.created})")
```

## Incident Response

In case of a security incident:

<Steps>
  <Step title="Contain the incident">
    * Immediately revoke compromised credentials
    * Disable affected user accounts
    * Block suspicious IP addresses at firewall level

    ```bash theme={null}
    # Disable a user
    curl -X PATCH https://awx.example.org/api/v2/users/12/ \
      -H "Authorization: Bearer <token>" \
      -d '{"is_active": false}'
    ```
  </Step>

  <Step title="Assess the impact">
    * Review activity stream for unauthorized actions
    * Check job history for suspicious playbook runs
    * Identify what systems were accessed
    * Determine if credentials were exfiltrated
  </Step>

  <Step title="Eradicate the threat">
    * Rotate all potentially compromised credentials
    * Update the SECRET\_KEY if database access was compromised
    * Apply security patches if a vulnerability was exploited
    * Reset passwords for affected accounts
  </Step>

  <Step title="Recover and restore">
    * Re-enable systems after verification
    * Monitor closely for recurring issues
    * Document the incident timeline
    * Update security procedures
  </Step>

  <Step title="Post-incident review">
    * Conduct root cause analysis
    * Update incident response procedures
    * Implement additional security controls
    * Train team on lessons learned
  </Step>
</Steps>

## See Also

* [Backup and Restore](/admin/backup-restore) — Protecting your AWX data
* [Configuration](/admin/configuration) — Authentication and settings configuration
* [AWX Credential Plugins](https://github.com/ansible/awx-custom-credential-plugin-example) — Custom secret management


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.