HTTPS API
VyOS provides a built-in HTTPS/HTTP server for the web interface and REST/GraphQL API. This makes it possible to automate router management and integrate it with external systems.
Overview
Key components
HTTPS Server:
- Nginx-based web server
- TLS/SSL encryption
- Support for self-signed and real certificates
- Virtual hosts (vhost)
API Endpoints:
- REST API: HTTP/JSON API for configuration and operations (VyOS 1.5+)
- GraphQL API: Flexible query-based API (VyOS 1.5+)
- Legacy API: Compatibility with VyOS 1.4
Authentication methods:
- API keys (recommended)
- Token-based authentication
VyOS 1.4 vs 1.5
VyOS 1.4 (Sagitta):
set service https api keys id <name> key '<api-key>'VyOS 1.5 (Circinus):
set service https api keys id <name> key '<api-key>'
set service https api rest # Enable REST API
set service https api graphql # Enable GraphQL API (optional)In VyOS 1.5 the REST and GraphQL endpoints are separate.
Basic configuration
Minimal HTTPS setup
# Basic HTTPS server
set service https listen-address 0.0.0.0
set service https port 443
# Self-signed certificate (for testing)
# VyOS generates one automatically on first startup
commit
saveEnabling the REST API (VyOS 1.5)
# HTTPS server
set service https listen-address 0.0.0.0
set service https port 443
# REST API
set service https api rest
set service https api keys id admin key 'MySecureAPIKey123!'
commit
saveEnabling the REST API (VyOS 1.4)
set service https listen-address 0.0.0.0
set service https port 443
# API (in 1.4 there is no rest/graphql split)
set service https api keys id admin key 'MySecureAPIKey123!'
commit
saveVerifying operation
# Check the service status
show service https
# Check the process
ps aux | grep nginx
# Check the ports
netstat -tlnp | grep 443
# Test the REST API
curl -k https://localhost/retrieve \
-H "Content-Type: application/json" \
-d '{"op": "showConfig", "path": ["system", "host-name"]}' \
-H "key: MySecureAPIKey123!"Certificate configuration
Self-signed certificate
VyOS automatically creates a self-signed certificate the first time HTTPS starts.
Generating a new one:
# Create a new self-signed certificate
generate pki certificate self-signed install vyos-https \
common-name "vyos.company.local" \
country "US" \
state "California" \
locality "San Francisco" \
organization "Company Inc" \
days 3650
# Apply it to HTTPS
set service https certificates certificate vyos-https
commit
saveCertificate from a CA
Using a certificate from a trusted CA (Let’s Encrypt, a corporate CA).
Preparing the certificates
You will need:
- Certificate (server.crt): The server certificate
- Private Key (server.key): The private key
- CA Certificate (ca.crt): The CA certificate (optional)
Importing the certificates
# Copy the files to the router
scp server.crt vyos@router:/tmp/
scp server.key vyos@router:/tmp/
scp ca.crt vyos@router:/tmp/
# On the router
configure
# Import the CA
generate pki ca import ca-production file /tmp/ca.crt
# Import the server certificate and key
generate pki certificate import vyos-https-prod \
certificate-file /tmp/server.crt \
private-key-file /tmp/server.key
# Apply it to HTTPS
set service https certificates certificate vyos-https-prod
commit
save
# Remove the temporary files
rm /tmp/server.crt /tmp/server.key /tmp/ca.crtLet’s Encrypt with Certbot
Automatically obtaining and renewing Let’s Encrypt certificates.
# Install Certbot (in the shell, not in configure)
sudo apt-get update
sudo apt-get install certbot
# Obtain the certificate (standalone mode)
# The HTTPS service must be temporarily stopped
sudo systemctl stop nginx
sudo certbot certonly --standalone -d vyos.example.com
# Import into VyOS
configure
generate pki certificate import letsencrypt-vyos \
certificate-file /etc/letsencrypt/live/vyos.example.com/fullchain.pem \
private-key-file /etc/letsencrypt/live/vyos.example.com/privkey.pem
set service https certificates certificate letsencrypt-vyos
commit
save
# Restart HTTPS
restart service httpsAutomatic renewal
Create the script /config/scripts/renew-cert.sh:
#!/bin/bash
# Stop nginx
systemctl stop nginx
# Renew the certificate
certbot renew --quiet
# Check the renewal
if [ $? -eq 0 ]; then
# Import the new certificate
/usr/libexec/vyos/conf_mode/pki.py import-cert \
letsencrypt-vyos \
/etc/letsencrypt/live/vyos.example.com/fullchain.pem \
/etc/letsencrypt/live/vyos.example.com/privkey.pem
# Restart nginx
systemctl restart nginx
else
# Start nginx even on error
systemctl start nginx
fiSetting up cron:
# Add to crontab (check every day at 3:00)
set system task-scheduler task renew-cert executable path '/config/scripts/renew-cert.sh'
set system task-scheduler task renew-cert interval 1d
set system task-scheduler task renew-cert start-time '03:00:00'
commitREST API
API structure
The REST API uses POST requests to the /configure, /retrieve, /show, etc. endpoints.
Base URL: https://<router-ip>/
Endpoints:
/retrieve- Retrieve configuration (showConfig)/configure- Change configuration (set, delete, comment)/show- Operational commands (show)/generate- Generate keys and certificates/reset- Reset operations/image- Manage system images/config-file- Save/load configuration/reboot,/poweroff- Power management
Authentication
# Header
key: MySecureAPIKey123!
# Or in the URL (not recommended)
?key=MySecureAPIKey123!REST API request examples
Retrieving configuration (retrieve)
# Hostname
curl -k https://192.168.1.1/retrieve \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "showConfig",
"path": ["system", "host-name"]
}'
# All interfaces
curl -k https://192.168.1.1/retrieve \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "showConfig",
"path": ["interfaces"]
}'
# The entire configuration
curl -k https://192.168.1.1/retrieve \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "showConfig",
"path": []
}'Response:
{
"success": true,
"data": {
"host-name": "vyos-router"
}
}Changing configuration (configure)
Set operation:
# Set the hostname
curl -k https://192.168.1.1/configure \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "set",
"path": ["system", "host-name", "vyos-gw"]
}'
# Set an IP address on an interface
curl -k https://192.168.1.1/configure \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "set",
"path": ["interfaces", "ethernet", "eth1", "address", "192.168.10.1/24"]
}'Delete operation:
curl -k https://192.168.1.1/configure \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "delete",
"path": ["interfaces", "ethernet", "eth1", "address", "192.168.10.1/24"]
}'Comment operation:
curl -k https://192.168.1.1/configure \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "comment",
"path": ["interfaces", "ethernet", "eth1"],
"value": "Management interface"
}'Batch operations
Multiple changes in a single request:
curl -k https://192.168.1.1/configure \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "set",
"path": ["interfaces", "ethernet", "eth2"],
"commands": [
{"op": "set", "path": ["address", "10.0.0.1/24"]},
{"op": "set", "path": ["description", "LAN"]},
{"op": "set", "path": ["mtu", "1500"]}
]
}'Show commands
# Show interfaces
curl -k https://192.168.1.1/show \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "show",
"path": ["interfaces"]
}'
# Show version
curl -k https://192.168.1.1/show \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "show",
"path": ["version"]
}'Generate operations
# Generate WireGuard key
curl -k https://192.168.1.1/generate \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "generate",
"path": ["wireguard", "default-keypair"]
}'Config-file operations
# Save configuration
curl -k https://192.168.1.1/config-file \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "save"
}'
# Load configuration
curl -k https://192.168.1.1/config-file \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "load",
"file": "/config/backup.config"
}'Power management
# Reboot
curl -k https://192.168.1.1/reboot \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "reboot",
"path": ["now"]
}'
# Poweroff
curl -k https://192.168.1.1/poweroff \
-H "Content-Type: application/json" \
-H "key: MySecureAPIKey123!" \
-d '{
"op": "poweroff",
"path": ["now"]
}'Python examples
import requests
import json
# Disable SSL warnings for self-signed certificates
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class VyOSAPI:
def __init__(self, host, api_key):
self.base_url = f"https://{host}"
self.headers = {
"Content-Type": "application/json",
"key": api_key
}
def show_config(self, path):
"""Retrieve configuration"""
data = {
"op": "showConfig",
"path": path
}
response = requests.post(
f"{self.base_url}/retrieve",
headers=self.headers,
json=data,
verify=False
)
return response.json()
def set_config(self, path, value=None):
"""Set configuration"""
data = {
"op": "set",
"path": path
}
if value:
data["value"] = value
response = requests.post(
f"{self.base_url}/configure",
headers=self.headers,
json=data,
verify=False
)
return response.json()
def delete_config(self, path):
"""Delete configuration"""
data = {
"op": "delete",
"path": path
}
response = requests.post(
f"{self.base_url}/configure",
headers=self.headers,
json=data,
verify=False
)
return response.json()
def save(self):
"""Save configuration"""
data = {"op": "save"}
response = requests.post(
f"{self.base_url}/config-file",
headers=self.headers,
json=data,
verify=False
)
return response.json()
def show(self, path):
"""Operational show command"""
data = {
"op": "show",
"path": path
}
response = requests.post(
f"{self.base_url}/show",
headers=self.headers,
json=data,
verify=False
)
return response.json()
# Usage
api = VyOSAPI("192.168.1.1", "MySecureAPIKey123!")
# Get the hostname
result = api.show_config(["system", "host-name"])
print(f"Hostname: {result['data']}")
# Set a new hostname
api.set_config(["system", "host-name", "new-hostname"])
api.save()
# Show interfaces
interfaces = api.show(["interfaces"])
print(json.dumps(interfaces, indent=2))GraphQL API (VyOS 1.5+)
The GraphQL API provides a more flexible, query-based interface.
Enabling GraphQL
set service https api graphql
set service https api keys id graphql-user key 'GraphQLKey123!'
commit
saveGraphQL Endpoint
URL: https://<router-ip>/graphql
GraphQL request examples
Retrieving configuration
curl -k https://192.168.1.1/graphql \
-H "Content-Type: application/json" \
-H "key: GraphQLKey123!" \
-d '{
"query": "{ systemStatus { hostname version uptime } }"
}'Changing configuration
curl -k https://192.168.1.1/graphql \
-H "Content-Type: application/json" \
-H "key: GraphQLKey123!" \
-d '{
"query": "mutation { configSet(path: \"system host-name\", value: \"vyos-gw\") { success errors } }"
}'The GraphQL documentation is available through the GraphiQL interface:
https://<router-ip>/graphql (in a browser)
Advanced configuration
Multiple API keys
# Admin full access
set service https api keys id admin key 'AdminKey123!'
# Read-only user (restricted only through the firewall)
set service https api keys id readonly key 'ReadOnlyKey456!'
# Monitoring system
set service https api keys id monitoring key 'MonitorKey789!'
commitNote: The VyOS API does not have built-in RBAC. Use separate keys and control access through the firewall.
Restricting API access
# Allow the API only from specific IPs
set firewall ipv4 input filter rule 100 action accept
set firewall ipv4 input filter rule 100 destination port 443
set firewall ipv4 input filter rule 100 protocol tcp
set firewall ipv4 input filter rule 100 source address 192.168.1.0/24
set firewall ipv4 input filter rule 100 description 'Allow HTTPS API from management network'
# Block everyone else
set firewall ipv4 input filter rule 109 action drop
set firewall ipv4 input filter rule 109 destination port 443
set firewall ipv4 input filter rule 109 protocol tcp
commitListening on specific interfaces
# Only on the management interface
set service https listen-address 192.168.1.1
# On several interfaces
set service https listen-address 192.168.1.1
set service https listen-address 10.0.0.1
commitChanging the port
# Non-standard port
set service https port 8443
commitVirtual hosts
# Vhost for different domains
set service https virtual-host api.example.com listen-address 0.0.0.0
set service https virtual-host api.example.com listen-port 443
set service https virtual-host api.example.com server-name api.example.com
# A separate certificate for the vhost
set service https virtual-host api.example.com certificates certificate api-cert
commitCORS settings
For web applications that call the API:
set service https api cors allow-origin '*'
# Or specific domains
set service https api cors allow-origin 'https://admin.example.com'
commitSecurity
Security recommendations
- Strong API keys:
# Generate a random key
openssl rand -base64 32
# Example: xK8pL9mN2qR5sT7vW0yZ3aB6cD9eF1gH2iJ4kL7mN0pQ=
set service https api keys id admin key 'xK8pL9mN2qR5sT7vW0yZ3aB6cD9eF1gH2iJ4kL7mN0pQ='Real TLS certificates:
- Use Let’s Encrypt or a corporate CA
- Do not use self-signed certificates in production
Access restriction:
# Firewall rules
set firewall ipv4 input filter rule 100 source address 192.168.1.0/24
# Listen only on the management interface
set service https listen-address 192.168.1.1- Rate limiting:
set firewall ipv4 input filter rule 100 recent count 10
set firewall ipv4 input filter rule 100 recent time minute 1- TLS version:
# Minimum version TLS 1.2
set service https tls-version '1.2'Disable HTTP (HTTPS only):
- By default VyOS uses HTTPS only
API key rotation:
- Rotate API keys regularly
- Use different keys for different applications
Logging API requests:
# Nginx logs
show log https
# Access log
cat /var/log/nginx/access.logAccess auditing
# View the Nginx access logs
show log https access
# Search for failed requests
cat /var/log/nginx/access.log | grep '401\|403'
# Active connections
netstat -tn | grep :443Integration with external systems
Ansible
---
- name: Configure VyOS via API
hosts: localhost
gather_facts: no
vars:
vyos_host: "192.168.1.1"
vyos_api_key: "MySecureAPIKey123!"
tasks:
- name: Set hostname
uri:
url: "https://{{ vyos_host }}/configure"
method: POST
headers:
key: "{{ vyos_api_key }}"
Content-Type: "application/json"
body_format: json
body:
op: "set"
path: ["system", "host-name", "vyos-ansible"]
validate_certs: no
register: result
- name: Save configuration
uri:
url: "https://{{ vyos_host }}/config-file"
method: POST
headers:
key: "{{ vyos_api_key }}"
Content-Type: "application/json"
body_format: json
body:
op: "save"
validate_certs: noTerraform
terraform {
required_providers {
vyos = {
source = "example/vyos"
version = "1.0.0"
}
}
}
provider "vyos" {
endpoint = "https://192.168.1.1"
api_key = "MySecureAPIKey123!"
}
resource "vyos_interface" "eth2" {
name = "eth2"
address = "10.0.0.1/24"
description = "LAN managed by Terraform"
}Monitoring (Prometheus)
# VyOS metrics exporter for Prometheus
from prometheus_client import start_http_server, Gauge
import requests
import time
# Metrics
interface_status = Gauge('vyos_interface_status', 'Interface status', ['interface'])
interface_rx_bytes = Gauge('vyos_interface_rx_bytes', 'RX bytes', ['interface'])
interface_tx_bytes = Gauge('vyos_interface_tx_bytes', 'TX bytes', ['interface'])
class VyOSExporter:
def __init__(self, vyos_host, api_key):
self.api = VyOSAPI(vyos_host, api_key)
def collect(self):
# Get interface statistics
result = self.api.show(["interfaces"])
for iface, data in result['data'].items():
interface_status.labels(interface=iface).set(1 if data['state'] == 'up' else 0)
interface_rx_bytes.labels(interface=iface).set(data['stats']['rx_bytes'])
interface_tx_bytes.labels(interface=iface).set(data['stats']['tx_bytes'])
if __name__ == '__main__':
exporter = VyOSExporter("192.168.1.1", "MySecureAPIKey123!")
start_http_server(9100)
while True:
exporter.collect()
time.sleep(15)Webhook for events
# Event handler for sending a webhook
set system event-handler route-change name 'route-webhook'
set system event-handler route-change script '/config/scripts/route-webhook.sh'
set system event-handler route-change pattern 'route added|route deleted'
commit/config/scripts/route-webhook.sh:
#!/bin/bash
EVENT="$1"
curl -X POST https://monitoring.example.com/webhook \
-H "Content-Type: application/json" \
-d "{\"event\": \"$EVENT\", \"router\": \"vyos-gw\", \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"Troubleshooting
HTTPS does not start
Problem: The HTTPS service fails to start.
Diagnostics:
# Check the status
show service https
# Nginx status
systemctl status nginx
# Logs
show log https
cat /var/log/nginx/error.log
# Check the nginx configuration
nginx -tPossible causes:
- Port 443 is occupied by another process
- Certificate issues
- A syntax error in the configuration
Solution:
# Check what is using the port
netstat -tlnp | grep :443
# Restart the service
restart service httpsThe API returns 401 Unauthorized
Problem: API requests fail authentication.
Causes:
- Incorrect API key
- API key not configured
- Incorrect header
Solution:
# Check the API keys
show service https api keys
# Make sure the key is passed in the header
curl -k https://192.168.1.1/retrieve \
-H "key: MySecureAPIKey123!" \
-vThe API returns 404
Problem: The endpoint is not found.
VyOS 1.5 specifics:
- In VyOS 1.5 you must explicitly enable the REST API:
set service https api rest
commitSlow API responses
Problem: API requests take a long time to complete.
Diagnostics:
# CPU load
show system cpu
# Memory
show system memory
# Process list
topSolution:
- Reduce the request rate
- Use batch operations
- Cache results on the client
SSL certificate issues
Problem: “SSL certificate problem: self signed certificate”.
Solution:
# In curl: use -k
curl -k https://...
# In Python: verify=False
requests.post(url, verify=False)
# Or import the CA certificate into the systemMonitoring and metrics
Nginx statistics
# Active connections
cat /var/run/nginx/nginx-status
# Access log analysis
cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn
# Requests per minute
cat /var/log/nginx/access.log | awk '{print $4}' | cut -d: -f1-3 | uniq -cAPI usage
# Number of API requests
grep "POST /configure" /var/log/nginx/access.log | wc -l
# Top API users (by IP)
grep "POST /" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn
# API errors
grep "HTTP/1.1\" [45]" /var/log/nginx/access.logUsage examples
Example 1: Automating DHCP configuration
import requests
import json
class DHCPManager:
def __init__(self, router, api_key):
self.router = router
self.api_key = api_key
self.base_url = f"https://{router}"
self.headers = {
"Content-Type": "application/json",
"key": api_key
}
def add_dhcp_static_mapping(self, network, name, mac, ip):
"""Add a static DHCP mapping"""
path = [
"service", "dhcp-server",
"shared-network-name", network,
"subnet", self.get_subnet(network),
"static-mapping", name,
"mac-address", mac
]
# Set MAC
self.api_call("/configure", {"op": "set", "path": path})
# Set IP
path[-1] = "ip-address"
path.append(ip)
self.api_call("/configure", {"op": "set", "path": path})
# Save
self.api_call("/config-file", {"op": "save"})
def api_call(self, endpoint, data):
response = requests.post(
f"{self.base_url}{endpoint}",
headers=self.headers,
json=data,
verify=False
)
return response.json()
def get_subnet(self, network):
# Get the subnet for the network
result = self.api_call("/retrieve", {
"op": "showConfig",
"path": ["service", "dhcp-server", "shared-network-name", network]
})
return list(result['data']['subnet'].keys())[0]
# Usage
dhcp = DHCPManager("192.168.1.1", "MySecureAPIKey123!")
dhcp.add_dhcp_static_mapping("LAN", "server1", "00:50:56:11:22:33", "192.168.1.10")Example 2: Configuration backup
#!/bin/bash
# backup-vyos.sh - Automatic configuration backup
ROUTER="192.168.1.1"
API_KEY="MySecureAPIKey123!"
BACKUP_DIR="/backup/vyos"
DATE=$(date +%Y%m%d-%H%M%S)
# Retrieve the configuration
curl -k https://${ROUTER}/retrieve \
-H "Content-Type: application/json" \
-H "key: ${API_KEY}" \
-d '{"op": "showConfig", "path": []}' \
> ${BACKUP_DIR}/config-${DATE}.json
# Retrieve the text configuration
curl -k https://${ROUTER}/retrieve \
-H "Content-Type: application/json" \
-H "key: ${API_KEY}" \
-d '{"op": "show", "path": ["configuration"]}' \
> ${BACKUP_DIR}/config-${DATE}.txt
# Remove old backups (>30 days)
find ${BACKUP_DIR} -type f -mtime +30 -delete
echo "Backup completed: ${BACKUP_DIR}/config-${DATE}.json"Example 3: Health check monitoring
#!/usr/bin/env python3
# vyos-health-check.py
import requests
import sys
import json
from datetime import datetime
class VyOSHealthCheck:
def __init__(self, router, api_key):
self.api = VyOSAPI(router, api_key)
self.status = {"healthy": True, "issues": []}
def check_interfaces(self):
"""Check the interface status"""
result = self.api.show(["interfaces"])
for iface, data in result['data'].items():
if data['admin_state'] == 'up' and data['state'] != 'up':
self.status["healthy"] = False
self.status["issues"].append(f"Interface {iface} is down")
def check_bgp(self):
"""Check the BGP neighbors"""
result = self.api.show(["bgp", "summary"])
for neighbor in result['data']['neighbors']:
if neighbor['state'] != 'Established':
self.status["healthy"] = False
self.status["issues"].append(f"BGP neighbor {neighbor['ip']} not established")
def check_vpn(self):
"""Check the VPN tunnels"""
result = self.api.show(["vpn", "ipsec", "sa"])
for tunnel in result['data']['tunnels']:
if tunnel['state'] != 'up':
self.status["healthy"] = False
self.status["issues"].append(f"VPN tunnel {tunnel['name']} is down")
def run(self):
"""Run all checks"""
self.check_interfaces()
self.check_bgp()
self.check_vpn()
# Output the result
print(json.dumps({
"timestamp": datetime.utcnow().isoformat(),
"healthy": self.status["healthy"],
"issues": self.status["issues"]
}, indent=2))
# Exit code
sys.exit(0 if self.status["healthy"] else 1)
if __name__ == '__main__':
checker = VyOSHealthCheck("192.168.1.1", "MySecureAPIKey123!")
checker.run()Best practices
- Use HTTPS - never use HTTP in production
- Strong API keys - at least 32 characters, random
- Real certificates - Let’s Encrypt or a corporate CA
- Restrict access - firewall rules for API endpoints
- Separate keys - use different API keys for different applications
- Rate limiting - protection against brute-force
- Logging - enable detailed logging of API requests
- Monitoring - track API usage
- Versioning - document the API version in your code
- Error handling - handle API errors correctly
- Timeout - set reasonable timeouts for API requests
- Retry logic - implement retries for transient errors
- Backup - automate configuration backups through the API
- Testing - test API integrations in a staging environment
- Documentation - document all API integrations
Conclusion
The HTTPS/REST API in VyOS provides a powerful tool for automation and integration. Proper security configuration (certificates, API keys, firewall) is critical for protecting your infrastructure.
Main use cases:
- Configuration automation through Ansible/Terraform
- Monitoring the router’s state
- Integration with external systems
- Self-service portals for network management
- CI/CD for network infrastructure as code
The VyOS API provides full access to the configuration and operational commands, making VyOS an ideal choice for modern automated network infrastructures.