opn_api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,979 @@
1
+ # opn_api
2
+
3
+ A Ruby client library and CLI tool for the OPNsense REST API.
4
+
5
+ #### Table of Contents
6
+
7
+ 1. [Overview](#overview)
8
+ 1. [Supported resources](#supported-resources)
9
+ 1. [Install](#install)
10
+ 1. [Modes](#modes)
11
+ 1. [CLI Usage](#cli-usage)
12
+ 1. [Configuration](#configuration)
13
+ 1. [Basics](#basics)
14
+ 1. [Table output and field filtering](#table-output-and-field-filtering)
15
+ 1. [Examples](#examples)
16
+ - [ACME Client](#acme-client)
17
+ - [Backup](#backup)
18
+ - [Cron](#cron)
19
+ - [DHC Relay](#dhc-relay)
20
+ - [Firewall](#firewall)
21
+ - [Gateways](#gateways)
22
+ - [Groups](#groups)
23
+ - [HA Sync](#ha-sync)
24
+ - [HAProxy](#haproxy)
25
+ - [IPsec](#ipsec)
26
+ - [Kea DHCP](#kea-dhcp)
27
+ - [Node Exporter](#node-exporter)
28
+ - [OpenVPN](#openvpn)
29
+ - [Plugins](#plugins)
30
+ - [Routes](#routes)
31
+ - [Service reconfigure](#service-reconfigure-1)
32
+ - [Snapshots](#snapshots)
33
+ - [Syslog](#syslog)
34
+ - [Trust certificates](#trust-certificates)
35
+ - [Tunables](#tunables)
36
+ - [Users](#users)
37
+ - [Zabbix](#zabbix)
38
+ - [Error handling](#error-handling)
39
+ - [Wrapper keys](#wrapper-keys)
40
+ - [Raw API access](#raw-api-access)
41
+ - [Ruby API](#ruby-api)
42
+ 1. [Features](#features)
43
+ - [Config loader](#config-loader)
44
+ - [ID resolver](#id-resolver)
45
+ - [Normalize](#normalize)
46
+ - [Resource CRUD](#resource-crud)
47
+ - [Resource registry](#resource-registry)
48
+ - [Service reconfigure](#service-reconfigure)
49
+ 1. [Development](#development)
50
+ - [Contributing](#contributing)
51
+ 1. [License](#license)
52
+
53
+ ## Overview
54
+
55
+ opn_api is a standalone Ruby library and command-line tool for communicating with [OPNsense](https://opnsense.org/) firewalls via their REST API. It is meant as a replacement for [opn-cli](https://github.com/andreas-stuerz/opn-cli). It provides:
56
+
57
+ - An HTTP client with SSL, redirect handling, and API key authentication
58
+ - UUID/name resolution for ModelRelationField and CertificateField references
59
+ - Service reconfigure orchestration with configtest support
60
+ - OPNsense selection-hash normalization
61
+ - A CLI tool for interactive API access
62
+ - A resource registry that abstracts away inconsistent endpoint naming
63
+
64
+ ## Supported resources
65
+
66
+ Backups and plugins are managed separately via dedicated commands (`backup`, `plugins`, `install`, `uninstall`).
67
+
68
+ | Resource | Manages |
69
+ |----------|---------|
70
+ | `acmeclient_account` | ACME Client accounts |
71
+ | `acmeclient_action` | ACME Client automation actions |
72
+ | `acmeclient_certificate` | ACME Client certificates |
73
+ | `acmeclient_settings` | ACME Client global settings (singleton) |
74
+ | `acmeclient_validation` | ACME Client validation methods |
75
+ | `cron` | Cron jobs |
76
+ | `dhcrelay` | DHCP Relay instances |
77
+ | `dhcrelay_destination` | DHCP Relay destinations |
78
+ | `firewall_alias` | Firewall aliases |
79
+ | `firewall_category` | Firewall categories |
80
+ | `firewall_group` | Firewall interface groups |
81
+ | `firewall_rule` | Firewall filter rules (new GUI) |
82
+ | `gateway` | Routing gateways |
83
+ | `group` | Local groups |
84
+ | `haproxy_acl` | HAProxy ACLs (conditions) |
85
+ | `haproxy_action` | HAProxy actions (rules) |
86
+ | `haproxy_backend` | HAProxy backend pools |
87
+ | `haproxy_cpu` | HAProxy CPU affinity / thread binding |
88
+ | `haproxy_errorfile` | HAProxy error files |
89
+ | `haproxy_fcgi` | HAProxy FastCGI applications |
90
+ | `haproxy_frontend` | HAProxy frontend listeners |
91
+ | `haproxy_group` | HAProxy user-list groups |
92
+ | `haproxy_healthcheck` | HAProxy health checks |
93
+ | `haproxy_lua` | HAProxy Lua scripts |
94
+ | `haproxy_mailer` | HAProxy mailers |
95
+ | `haproxy_mapfile` | HAProxy map files |
96
+ | `haproxy_resolver` | HAProxy DNS resolvers |
97
+ | `haproxy_server` | HAProxy backend servers |
98
+ | `haproxy_settings` | HAProxy global settings (singleton) |
99
+ | `haproxy_user` | HAProxy user-list users |
100
+ | `hasync` | HA sync / CARP settings (singleton) |
101
+ | `ipsec_child` | IPsec child SAs (Swanctl) |
102
+ | `ipsec_connection` | IPsec connections (Swanctl) |
103
+ | `ipsec_keypair` | IPsec key pairs (Swanctl) |
104
+ | `ipsec_local` | IPsec local authentication (Swanctl) |
105
+ | `ipsec_pool` | IPsec address pools (Swanctl) |
106
+ | `ipsec_presharedkey` | IPsec pre-shared keys (Swanctl) |
107
+ | `ipsec_remote` | IPsec remote authentication (Swanctl) |
108
+ | `ipsec_settings` | IPsec global settings (singleton) |
109
+ | `ipsec_vti` | IPsec VTI entries (Swanctl) |
110
+ | `kea_ctrl_agent` | KEA Control Agent settings (singleton) |
111
+ | `kea_dhcpv4` | KEA DHCPv4 global settings (singleton) |
112
+ | `kea_dhcpv4_peer` | KEA DHCPv4 HA peers |
113
+ | `kea_dhcpv4_reservation` | KEA DHCPv4 reservations |
114
+ | `kea_dhcpv4_subnet` | KEA DHCPv4 subnets |
115
+ | `kea_dhcpv6` | KEA DHCPv6 global settings (singleton) |
116
+ | `kea_dhcpv6_pd_pool` | KEA DHCPv6 prefix delegation pools |
117
+ | `kea_dhcpv6_peer` | KEA DHCPv6 HA peers |
118
+ | `kea_dhcpv6_reservation` | KEA DHCPv6 reservations |
119
+ | `kea_dhcpv6_subnet` | KEA DHCPv6 subnets |
120
+ | `node_exporter` | Prometheus Node Exporter settings (singleton) |
121
+ | `openvpn_cso` | OpenVPN client-specific overrides |
122
+ | `openvpn_instance` | OpenVPN instances |
123
+ | `openvpn_statickey` | OpenVPN static keys |
124
+ | `route` | Static routes |
125
+ | `snapshot` | ZFS snapshots |
126
+ | `syslog` | Syslog remote destinations |
127
+ | `trust_ca` | Trust Certificate Authorities |
128
+ | `trust_cert` | Trust certificates |
129
+ | `trust_crl` | Trust Certificate Revocation Lists |
130
+ | `tunable` | System tunables (sysctl) |
131
+ | `user` | Local users |
132
+ | `zabbix_agent` | Zabbix Agent settings (singleton) |
133
+ | `zabbix_agent_alias` | Zabbix Agent Alias entries |
134
+ | `zabbix_agent_userparameter` | Zabbix Agent UserParameter entries |
135
+ | `zabbix_proxy` | Zabbix Proxy settings (singleton) |
136
+
137
+ ## Install
138
+
139
+ ```
140
+ gem install opn_api
141
+ ```
142
+
143
+ Or add to your Gemfile:
144
+
145
+ ```ruby
146
+ gem 'opn_api'
147
+ ```
148
+
149
+ ## Modes
150
+
151
+ opn_api can be used in 2 modes: CLI mode and Ruby API mode.
152
+
153
+ CLI mode provides the `opn-api` command for interactive use and shell scripts. Ruby API mode allows direct integration into Ruby projects.
154
+
155
+ ## CLI Usage
156
+
157
+ ```
158
+ $ opn-api --help
159
+
160
+ Usage: opn-api [options] <command> [command-options] [args...]
161
+
162
+ A CLI tool for the OPNsense REST API.
163
+
164
+ Global options:
165
+ -c, --config-dir PATH Config directory for device files
166
+ -d, --device NAME Device name (default: "default")
167
+ -f, --format FORMAT Output format: table, json, yaml (default: table)
168
+ -F, --fields FIELDS Comma-separated field names for table output
169
+ -A, --all-fields Show all fields in table output (default: first 5)
170
+ -E, --show-empty Show empty fields in table output (default: hidden)
171
+ -v, --verbose Enable debug output (includes API error details)
172
+ --version Show version
173
+ -h, --help Show this help
174
+
175
+ Commands:
176
+ backup Download config backup
177
+ create Create resource
178
+ delete Delete resource
179
+ devices List configured devices
180
+ get GET request to API path
181
+ groups List reconfigure groups
182
+ install Install plugin
183
+ plugins List installed plugins
184
+ post POST request to API path
185
+ reconfigure Trigger service reconfigure
186
+ resources List known resource types
187
+ search Search resources
188
+ show Show single resource
189
+ test Test device connectivity
190
+ uninstall Uninstall plugin
191
+ update Update resource
192
+ ```
193
+
194
+ Resource commands (`search`, `show`, `create`, `update`, `delete`) accept a resource name from the built-in registry. Run `opn-api resources` for a full list. Singleton resources (settings) are auto-detected and work without UUID for `show` and `update`.
195
+
196
+ ## Configuration
197
+
198
+ Device credentials are stored in YAML files, one per OPNsense device. The config directory is searched in this order (highest priority last):
199
+
200
+ 1. `/etc/opn-api/devices/` (system-wide)
201
+ 2. `~/.config/opn-api/devices/` (per-user)
202
+ 3. Explicit `config_dir` parameter or `-c` CLI flag
203
+ 4. `OPN_API_CONFIG_DIR` environment variable (override)
204
+
205
+ ### Device file format
206
+
207
+ Each device is a YAML file named `<device_name>.yaml`:
208
+
209
+ ```yaml
210
+ # ~/.config/opn-api/devices/opnsense01.yaml
211
+ url: https://192.168.1.1/api
212
+ api_key: +OPNSENSE_API_KEY
213
+ api_secret: +OPNSENSE_API_SECRET
214
+ ssl_verify: false
215
+ timeout: 60
216
+ ```
217
+
218
+ This format is compatible with [puppet-opn](https://github.com/markt-de/puppet-opn) device files, so existing Puppet configurations can be reused by pointing `config_dir` to the Puppet config directory.
219
+
220
+ ## Basics
221
+
222
+ ```
223
+ # List configured devices
224
+ $ opn-api devices
225
+
226
+ # Test connectivity
227
+ $ opn-api -d opnsense01 test
228
+
229
+ # List known resource types (shows name, wrapper key, type)
230
+ $ opn-api resources
231
+ ```
232
+
233
+ ## Table output and field filtering
234
+
235
+ By default, the table output shows only the first 5 fields to keep it readable. Use `-F` to select specific fields or `-A` to show all fields. Fields with empty values are hidden by default — use `-E` to show them. JSON and YAML output (`-f json`, `-f yaml`) always includes all data unmodified.
236
+
237
+ ```
238
+ # Search resources (default: first 5 fields shown)
239
+ $ opn-api -d opnsense01 search firewall_alias
240
+
241
+ # Select specific fields
242
+ $ opn-api -d opnsense01 -F uuid,enabled,name,type search firewall_alias
243
+
244
+ # Show all fields
245
+ $ opn-api -d opnsense01 -A search firewall_alias
246
+
247
+ # Show empty fields (hidden by default)
248
+ $ opn-api -d opnsense01 -E show acmeclient_settings
249
+
250
+ # JSON output always includes all data (unaffected by -F/-A/-E)
251
+ $ opn-api -d opnsense01 -f json search haproxy_server
252
+ ```
253
+
254
+ ## Examples
255
+
256
+ ### ACME Client
257
+
258
+ ```
259
+ # List ACME accounts
260
+ $ opn-api -d opnsense01 search acmeclient_account
261
+
262
+ # List ACME certificates
263
+ $ opn-api -d opnsense01 search acmeclient_certificate
264
+
265
+ # List ACME validations
266
+ $ opn-api -d opnsense01 search acmeclient_validation
267
+
268
+ # List ACME actions
269
+ $ opn-api -d opnsense01 search acmeclient_action
270
+
271
+ # Show ACME settings (singleton)
272
+ $ opn-api -d opnsense01 show acmeclient_settings
273
+
274
+ # Update ACME settings (wrapper key: "acmeclient")
275
+ $ opn-api -d opnsense01 update acmeclient_settings \
276
+ -j '{"acmeclient":{"settings":{"environment":"production"}}}'
277
+ ```
278
+
279
+ ### Backup
280
+
281
+ Download the OPNsense configuration backup (XML). The backup endpoint returns XML instead of JSON, so this command uses a raw (non-JSON) mode internally.
282
+
283
+ ```
284
+ # Download backup to file
285
+ $ opn-api -d opnsense01 backup /tmp/opnsense01-backup.xml
286
+
287
+ # Print backup XML to stdout (e.g. for piping)
288
+ $ opn-api -d opnsense01 backup > /tmp/opnsense01-backup.xml
289
+ ```
290
+
291
+ Ruby API:
292
+
293
+ ```ruby
294
+ # Download backup as raw XML string
295
+ config = OpnApi::Config.new
296
+ client = config.client_for('opnsense01')
297
+ xml = client.get('core/backup/download/this', raw: true)
298
+ File.write('/tmp/backup.xml', xml)
299
+ ```
300
+
301
+ ### Cron
302
+
303
+ ```
304
+ # List cron jobs
305
+ $ opn-api -d opnsense01 search cron
306
+
307
+ # Show cron job details
308
+ $ opn-api -d opnsense01 show cron a1b2c3d4-...
309
+
310
+ # Create cron job (wrapper key: "job")
311
+ $ opn-api -d opnsense01 create cron \
312
+ -j '{"job":{"enabled":"1","minutes":"0","hours":"3","description":"nightly backup"}}'
313
+
314
+ # Delete cron job
315
+ $ opn-api -d opnsense01 delete cron a1b2c3d4-...
316
+ ```
317
+
318
+ ### DHC Relay
319
+
320
+ ```
321
+ # List DHC relays
322
+ $ opn-api -d opnsense01 search dhcrelay
323
+
324
+ # List DHC relay destinations
325
+ $ opn-api -d opnsense01 search dhcrelay_destination
326
+
327
+ # Create relay destination (wrapper key: "destination")
328
+ $ opn-api -d opnsense01 create dhcrelay_destination \
329
+ -j '{"destination":{"server":"10.0.0.5"}}'
330
+ ```
331
+
332
+ ### Firewall
333
+
334
+ ```
335
+ # List all aliases
336
+ $ opn-api -d opnsense01 search firewall_alias
337
+
338
+ # Show single alias by UUID
339
+ $ opn-api -d opnsense01 show firewall_alias 8a2f3b4c-...
340
+
341
+ # Create alias (wrapper key: "alias")
342
+ $ opn-api -d opnsense01 create firewall_alias \
343
+ -j '{"alias":{"name":"test","type":"host","content":"10.0.0.1","enabled":"1"}}'
344
+
345
+ # Create via stdin
346
+ $ echo '{"alias":{"name":"test","type":"host","content":"10.0.0.1","enabled":"1"}}' \
347
+ | opn-api -d opnsense01 create firewall_alias
348
+
349
+ # Update alias
350
+ $ opn-api -d opnsense01 update firewall_alias 8a2f3b4c-... \
351
+ -j '{"alias":{"content":"10.0.0.2"}}'
352
+
353
+ # Delete alias
354
+ $ opn-api -d opnsense01 delete firewall_alias 8a2f3b4c-...
355
+
356
+ # List firewall rules
357
+ $ opn-api -d opnsense01 search firewall_rule
358
+
359
+ # List firewall categories
360
+ $ opn-api -d opnsense01 search firewall_category
361
+
362
+ # List firewall groups
363
+ $ opn-api -d opnsense01 search firewall_group
364
+ ```
365
+
366
+ ### Gateways
367
+
368
+ ```
369
+ # List all gateways
370
+ $ opn-api -d opnsense01 search gateway
371
+
372
+ # Show gateway details
373
+ $ opn-api -d opnsense01 show gateway a1b2c3d4-...
374
+
375
+ # Create gateway (wrapper key: "gateway_item")
376
+ $ opn-api -d opnsense01 create gateway \
377
+ -j '{"gateway_item":{"name":"WAN_GW","interface":"wan","gateway":"192.168.1.1"}}'
378
+ ```
379
+
380
+ ### Groups
381
+
382
+ ```
383
+ # List all groups
384
+ $ opn-api -d opnsense01 search group
385
+
386
+ # Show group details
387
+ $ opn-api -d opnsense01 show group a1b2c3d4-...
388
+
389
+ # Create group (wrapper key: "group")
390
+ $ opn-api -d opnsense01 create group \
391
+ -j '{"group":{"name":"admins","description":"Admin group"}}'
392
+ ```
393
+
394
+ ### HA Sync
395
+
396
+ HA Sync is a singleton resource (one per device).
397
+
398
+ ```
399
+ # Show HA Sync settings (singleton)
400
+ $ opn-api -d opnsense01 show hasync
401
+
402
+ # Update HA Sync settings (wrapper key: "hasync")
403
+ $ opn-api -d opnsense01 update hasync \
404
+ -j '{"hasync":{"pfsyncenabled":"1","synchronizeinterface":"lan"}}'
405
+ ```
406
+
407
+ ### HAProxy
408
+
409
+ #### Servers
410
+
411
+ ```
412
+ # List all servers
413
+ $ opn-api -d opnsense01 search haproxy_server
414
+
415
+ # Show server details
416
+ $ opn-api -d opnsense01 show haproxy_server 1a2b3c4d-...
417
+
418
+ # Create server (wrapper key: "server")
419
+ $ opn-api -d opnsense01 create haproxy_server \
420
+ -j '{"server":{"name":"web01","address":"10.0.0.10","port":"8080"}}'
421
+
422
+ # Update server
423
+ $ opn-api -d opnsense01 update haproxy_server 1a2b3c4d-... \
424
+ -j '{"server":{"port":"8443"}}'
425
+
426
+ # Delete server
427
+ $ opn-api -d opnsense01 delete haproxy_server 1a2b3c4d-...
428
+ ```
429
+
430
+ #### Backends
431
+
432
+ ```
433
+ # List all backends
434
+ $ opn-api -d opnsense01 search haproxy_backend
435
+
436
+ # Show backend details
437
+ $ opn-api -d opnsense01 show haproxy_backend 2b3c4d5e-...
438
+
439
+ # Create backend (wrapper key: "backend")
440
+ $ opn-api -d opnsense01 create haproxy_backend \
441
+ -j '{"backend":{"name":"web_pool","mode":"http","linkedServers":"1a2b3c4d-..."}}'
442
+
443
+ # Update backend
444
+ $ opn-api -d opnsense01 update haproxy_backend 2b3c4d5e-... \
445
+ -j '{"backend":{"mode":"tcp"}}'
446
+
447
+ # Delete backend
448
+ $ opn-api -d opnsense01 delete haproxy_backend 2b3c4d5e-...
449
+ ```
450
+
451
+ #### Frontends
452
+
453
+ ```
454
+ # List all frontends
455
+ $ opn-api -d opnsense01 search haproxy_frontend
456
+
457
+ # Show frontend details
458
+ $ opn-api -d opnsense01 show haproxy_frontend 3c4d5e6f-...
459
+
460
+ # Create frontend (wrapper key: "frontend")
461
+ $ opn-api -d opnsense01 create haproxy_frontend \
462
+ -j '{"frontend":{"name":"https_in","bind":"0.0.0.0:443","mode":"http","defaultBackend":"2b3c4d5e-..."}}'
463
+
464
+ # Update frontend
465
+ $ opn-api -d opnsense01 update haproxy_frontend 3c4d5e6f-... \
466
+ -j '{"frontend":{"bind":"0.0.0.0:8443"}}'
467
+
468
+ # Delete frontend
469
+ $ opn-api -d opnsense01 delete haproxy_frontend 3c4d5e6f-...
470
+ ```
471
+
472
+ #### Settings and other sub-resources
473
+
474
+ ```
475
+ # Show HAProxy settings (singleton)
476
+ $ opn-api -d opnsense01 show haproxy_settings
477
+
478
+ # Update HAProxy settings (wrapper key: "haproxy")
479
+ $ opn-api -d opnsense01 update haproxy_settings \
480
+ -j '{"haproxy":{"general":{"tuning":{"maxconn":"2000"}}}}'
481
+
482
+ # List other HAProxy sub-resources
483
+ $ opn-api -d opnsense01 search haproxy_acl
484
+ $ opn-api -d opnsense01 search haproxy_action
485
+ $ opn-api -d opnsense01 search haproxy_cpu
486
+ $ opn-api -d opnsense01 search haproxy_errorfile
487
+ $ opn-api -d opnsense01 search haproxy_fcgi
488
+ $ opn-api -d opnsense01 search haproxy_group
489
+ $ opn-api -d opnsense01 search haproxy_healthcheck
490
+ $ opn-api -d opnsense01 search haproxy_lua
491
+ $ opn-api -d opnsense01 search haproxy_mailer
492
+ $ opn-api -d opnsense01 search haproxy_mapfile
493
+ $ opn-api -d opnsense01 search haproxy_resolver
494
+ $ opn-api -d opnsense01 search haproxy_user
495
+
496
+ # Apply changes (includes configtest)
497
+ $ opn-api -d opnsense01 reconfigure haproxy
498
+ ```
499
+
500
+ ### IPsec
501
+
502
+ ```
503
+ # List all IPsec connections
504
+ $ opn-api -d opnsense01 search ipsec_connection
505
+
506
+ # Show connection details
507
+ $ opn-api -d opnsense01 show ipsec_connection 4d5e6f7a-...
508
+
509
+ # Full JSON output
510
+ $ opn-api -d opnsense01 -f json show ipsec_connection 4d5e6f7a-...
511
+
512
+ # List other IPsec sub-resources
513
+ $ opn-api -d opnsense01 search ipsec_child
514
+ $ opn-api -d opnsense01 search ipsec_keypair
515
+ $ opn-api -d opnsense01 search ipsec_local
516
+ $ opn-api -d opnsense01 search ipsec_pool
517
+ $ opn-api -d opnsense01 search ipsec_presharedkey
518
+ $ opn-api -d opnsense01 search ipsec_remote
519
+ $ opn-api -d opnsense01 search ipsec_vti
520
+
521
+ # Show IPsec settings (singleton)
522
+ $ opn-api -d opnsense01 show ipsec_settings
523
+
524
+ # Apply changes
525
+ $ opn-api -d opnsense01 reconfigure ipsec
526
+ ```
527
+
528
+ ### Kea DHCP
529
+
530
+ ```
531
+ # Show Kea DHCPv4 settings (singleton)
532
+ $ opn-api -d opnsense01 show kea_dhcpv4
533
+
534
+ # List DHCPv4 subnets
535
+ $ opn-api -d opnsense01 search kea_dhcpv4_subnet
536
+
537
+ # Create DHCPv4 subnet (wrapper key: "subnet4")
538
+ $ opn-api -d opnsense01 create kea_dhcpv4_subnet \
539
+ -j '{"subnet4":{"subnet":"10.0.1.0/24"}}'
540
+
541
+ # List DHCPv4 reservations
542
+ $ opn-api -d opnsense01 search kea_dhcpv4_reservation
543
+
544
+ # List DHCPv4 HA peers
545
+ $ opn-api -d opnsense01 search kea_dhcpv4_peer
546
+
547
+ # Show Kea DHCPv6 settings (singleton)
548
+ $ opn-api -d opnsense01 show kea_dhcpv6
549
+
550
+ # List DHCPv6 subnets, reservations, PD pools, peers
551
+ $ opn-api -d opnsense01 search kea_dhcpv6_subnet
552
+ $ opn-api -d opnsense01 search kea_dhcpv6_reservation
553
+ $ opn-api -d opnsense01 search kea_dhcpv6_pd_pool
554
+ $ opn-api -d opnsense01 search kea_dhcpv6_peer
555
+
556
+ # Show Kea control agent settings (singleton)
557
+ $ opn-api -d opnsense01 show kea_ctrl_agent
558
+ ```
559
+
560
+ ### Node Exporter
561
+
562
+ Node Exporter is a singleton resource.
563
+
564
+ ```
565
+ # Show Node Exporter settings (singleton)
566
+ $ opn-api -d opnsense01 show node_exporter
567
+
568
+ # Update Node Exporter settings (wrapper key: "general")
569
+ $ opn-api -d opnsense01 update node_exporter \
570
+ -j '{"general":{"listen_address":"0.0.0.0","listen_port":"9100"}}'
571
+ ```
572
+
573
+ ### OpenVPN
574
+
575
+ ```
576
+ # List all OpenVPN instances
577
+ $ opn-api -d opnsense01 search openvpn_instance
578
+
579
+ # Show instance details
580
+ $ opn-api -d opnsense01 show openvpn_instance 5e6f7a8b-...
581
+
582
+ # Full JSON output
583
+ $ opn-api -d opnsense01 -f json show openvpn_instance 5e6f7a8b-...
584
+
585
+ # List client-specific overrides
586
+ $ opn-api -d opnsense01 search openvpn_cso
587
+
588
+ # List static keys
589
+ $ opn-api -d opnsense01 search openvpn_statickey
590
+
591
+ # Apply changes
592
+ $ opn-api -d opnsense01 reconfigure openvpn
593
+ ```
594
+
595
+ ### Plugins
596
+
597
+ ```
598
+ # List installed plugins
599
+ $ opn-api -d opnsense01 plugins
600
+
601
+ # Install a plugin
602
+ $ opn-api -d opnsense01 install os-haproxy
603
+
604
+ # Uninstall a plugin
605
+ $ opn-api -d opnsense01 uninstall os-haproxy
606
+ ```
607
+
608
+ Note: Install/uninstall are asynchronous — the API returns immediately while the operation continues in the background.
609
+
610
+ ### Routes
611
+
612
+ ```
613
+ # List all routes
614
+ $ opn-api -d opnsense01 search route
615
+
616
+ # Show route details
617
+ $ opn-api -d opnsense01 show route a1b2c3d4-...
618
+
619
+ # Create route (wrapper key: "route")
620
+ $ opn-api -d opnsense01 create route \
621
+ -j '{"route":{"network":"10.0.2.0/24","gateway":"WAN_GW","descr":"office network"}}'
622
+
623
+ # Apply changes
624
+ $ opn-api -d opnsense01 reconfigure route
625
+ ```
626
+
627
+ ### Service reconfigure
628
+
629
+ After creating, updating, or deleting resources, trigger a service reconfigure to apply the changes.
630
+
631
+ ```
632
+ # Trigger HAProxy reconfigure (includes configtest)
633
+ $ opn-api -d opnsense01 reconfigure haproxy
634
+
635
+ # Trigger IPsec reconfigure
636
+ $ opn-api -d opnsense01 reconfigure ipsec
637
+
638
+ # Trigger OpenVPN reconfigure
639
+ $ opn-api -d opnsense01 reconfigure openvpn
640
+
641
+ # Trigger tunable reconfigure
642
+ $ opn-api -d opnsense01 reconfigure tunable
643
+
644
+ # Trigger Zabbix agent reconfigure
645
+ $ opn-api -d opnsense01 reconfigure zabbix_agent
646
+
647
+ # List all available reconfigure groups
648
+ $ opn-api groups
649
+ ```
650
+
651
+ ### Snapshots
652
+
653
+ ```
654
+ # List all snapshots (uses GET-based search)
655
+ $ opn-api -d opnsense01 search snapshot
656
+ ```
657
+
658
+ ### Syslog
659
+
660
+ ```
661
+ # List syslog destinations
662
+ $ opn-api -d opnsense01 search syslog
663
+
664
+ # Show syslog destination details
665
+ $ opn-api -d opnsense01 show syslog a1b2c3d4-...
666
+
667
+ # Create syslog destination (wrapper key: "destination")
668
+ $ opn-api -d opnsense01 create syslog \
669
+ -j '{"destination":{"enabled":"1","transport":"udp4","hostname":"10.0.0.100","port":"514"}}'
670
+
671
+ # Apply changes
672
+ $ opn-api -d opnsense01 reconfigure syslog
673
+ ```
674
+
675
+ ### Trust certificates
676
+
677
+ ```
678
+ # List all certificates
679
+ $ opn-api -d opnsense01 search trust_cert
680
+
681
+ # Show certificate details
682
+ $ opn-api -d opnsense01 show trust_cert 6f7a8b9c-...
683
+
684
+ # List all CAs
685
+ $ opn-api -d opnsense01 search trust_ca
686
+
687
+ # Show CA details
688
+ $ opn-api -d opnsense01 show trust_ca 7a8b9c0d-...
689
+
690
+ # List CRLs (uses GET-based search)
691
+ $ opn-api -d opnsense01 search trust_crl
692
+ ```
693
+
694
+ ### Tunables
695
+
696
+ The wrapper key for tunables is `sysctl` (not `tunable` or `item`).
697
+
698
+ ```
699
+ # List all tunables
700
+ $ opn-api -d opnsense01 search tunable
701
+
702
+ # Show tunable details
703
+ $ opn-api -d opnsense01 show tunable a1b2c3d4-...
704
+
705
+ # Create tunable (wrapper key: "sysctl")
706
+ $ opn-api -d opnsense01 create tunable \
707
+ -j '{"sysctl":{"tunable":"net.inet.ip.forwarding","value":"1"}}'
708
+
709
+ # Update tunable
710
+ $ opn-api -d opnsense01 update tunable a1b2c3d4-... \
711
+ -j '{"sysctl":{"value":"0"}}'
712
+
713
+ # Delete tunable
714
+ $ opn-api -d opnsense01 delete tunable a1b2c3d4-...
715
+
716
+ # Apply changes
717
+ $ opn-api -d opnsense01 reconfigure tunable
718
+ ```
719
+
720
+ ### Users
721
+
722
+ ```
723
+ # List all users
724
+ $ opn-api -d opnsense01 search user
725
+
726
+ # Show user details
727
+ $ opn-api -d opnsense01 show user a1b2c3d4-...
728
+
729
+ # Create user (wrapper key: "user")
730
+ $ opn-api -d opnsense01 create user \
731
+ -j '{"user":{"name":"testuser","email":"test@example.com"}}'
732
+ ```
733
+
734
+ ### Zabbix
735
+
736
+ #### Zabbix agent
737
+
738
+ Zabbix agent is a singleton resource (one per device).
739
+
740
+ ```
741
+ # Show Zabbix agent settings (singleton)
742
+ $ opn-api -d opnsense01 show zabbix_agent
743
+
744
+ # Show as JSON
745
+ $ opn-api -d opnsense01 -f json show zabbix_agent
746
+
747
+ # Update Zabbix agent settings (wrapper key: "zabbixagent")
748
+ $ opn-api -d opnsense01 update zabbix_agent \
749
+ -j '{"zabbixagent":{"settings":{"main":{"enabled":"1","hostname":"opnsense01","serverList":"10.0.0.5"}}}}'
750
+
751
+ # Apply changes
752
+ $ opn-api -d opnsense01 reconfigure zabbix_agent
753
+ ```
754
+
755
+ #### Zabbix agent sub-resources
756
+
757
+ Zabbix agent aliases and user parameters are CRUD resources within the Zabbix agent settings.
758
+
759
+ ```
760
+ # List Zabbix agent aliases (uses GET-based search)
761
+ $ opn-api -d opnsense01 search zabbix_agent_alias
762
+
763
+ # Create alias (wrapper key: "alias")
764
+ $ opn-api -d opnsense01 create zabbix_agent_alias \
765
+ -j '{"alias":{"key":"system.uptime","item":"system.uptime"}}'
766
+
767
+ # List Zabbix agent user parameters
768
+ $ opn-api -d opnsense01 search zabbix_agent_userparameter
769
+ ```
770
+
771
+ #### Zabbix proxy
772
+
773
+ Zabbix proxy is a singleton resource.
774
+
775
+ ```
776
+ # Show Zabbix proxy settings (singleton)
777
+ $ opn-api -d opnsense01 show zabbix_proxy
778
+
779
+ # Update Zabbix proxy settings (wrapper key: "general")
780
+ $ opn-api -d opnsense01 update zabbix_proxy \
781
+ -j '{"general":{"enabled":"1","hostname":"zbxproxy01"}}'
782
+ ```
783
+
784
+ ### Error handling
785
+
786
+ On failure, the full API response is shown as JSON for debugging:
787
+
788
+ ```
789
+ $ opn-api -d opnsense01 create firewall_alias -j '{"alias":{"name":"test"}}'
790
+ Error: create failed: {"result":"failed","validations":{"alias.type":"This field is required."}}
791
+ ```
792
+
793
+ When OPNsense returns only `{"result":"failed"}` without validation details, the most common cause is a wrong wrapper key. The error message includes the expected wrapper key from the registry:
794
+
795
+ ```
796
+ # Wrong wrapper key — error shows expected key
797
+ $ opn-api -d opnsense01 create firewall_alias -j '{"item":{"name":"test"}}'
798
+ Error: create failed: {"result":"failed"}
799
+ Hint: The JSON wrapper key is likely wrong. Expected wrapper key: 'alias'.
800
+
801
+ # Use -v to see full request/response details for debugging
802
+ $ opn-api -v -d opnsense01 create firewall_alias -j '{"alias":{"name":"test"}}'
803
+ ```
804
+
805
+ ### Wrapper keys
806
+
807
+ The OPNsense API uses different keys for endpoints and POST body wrappers. The wrapper key wraps the config in the JSON body. When using registry resource names, `opn-api resources` shows the correct wrapper key for each type.
808
+
809
+ Common examples:
810
+
811
+ | Resource name | Wrapper key |
812
+ |---|---|
813
+ | `cron` | `job` |
814
+ | `dhcrelay` | `relay` |
815
+ | `firewall_alias` | `alias` |
816
+ | `firewall_rule` | `rule` |
817
+ | `gateway` | `gateway_item` |
818
+ | `haproxy_backend` | `backend` |
819
+ | `haproxy_frontend` | `frontend` |
820
+ | `haproxy_server` | `server` |
821
+ | `ipsec_connection` | `connection` |
822
+ | `ipsec_keypair` | `keyPair` |
823
+ | `kea_dhcpv4_subnet` | `subnet4` |
824
+ | `openvpn_instance` | `instance` |
825
+ | `route` | `route` |
826
+ | `syslog` | `destination` |
827
+ | `trust_ca` | `ca` |
828
+ | `trust_cert` | `cert` |
829
+ | `tunable` | `sysctl` |
830
+ | `user` | `user` |
831
+
832
+ To find the correct wrapper key for any resource, use `show` to inspect an existing item — the top-level key in the raw JSON response (`-f json`) is the wrapper key.
833
+
834
+ ### Raw API access
835
+
836
+ For endpoints not covered by the resource registry, use the `get` and `post` commands for direct API access.
837
+
838
+ ```
839
+ # GET request to any API path
840
+ $ opn-api -d opnsense01 get core/firmware/info
841
+
842
+ # POST request with JSON body
843
+ $ opn-api -d opnsense01 post firewall/alias/search_item '{}'
844
+ ```
845
+
846
+ ### Ruby API
847
+
848
+ ```ruby
849
+ #!/usr/bin/env ruby
850
+
851
+ require 'opn_api'
852
+
853
+ # Create client from config file
854
+ config = OpnApi::Config.new
855
+ client = config.client_for('opnsense01')
856
+
857
+ # Or create client directly
858
+ client = OpnApi::Client.new(
859
+ url: 'https://fw.example.com/api',
860
+ api_key: '+ABC...',
861
+ api_secret: '+XYZ...',
862
+ ssl_verify: false,
863
+ )
864
+
865
+ # Simple API calls
866
+ info = client.get('core/firmware/info')
867
+ puts "OPNsense version: #{info['product_version']}"
868
+
869
+ aliases = client.post('firewall/alias/search_item', {})
870
+ aliases['rows'].each { |a| puts a['name'] }
871
+
872
+ # CRUD via resource registry (preferred — handles endpoint quirks)
873
+ res = OpnApi::ResourceRegistry.build(client, 'haproxy_server')
874
+ servers = res.search
875
+ new_server = res.add('server' => { 'name' => 'web01', 'address' => '10.0.0.10', 'port' => '8080' })
876
+ res.set(new_server['uuid'], 'server' => { 'port' => '8443' })
877
+ res.del(new_server['uuid'])
878
+
879
+ # Singleton resources (settings)
880
+ settings = OpnApi::ResourceRegistry.build(client, 'haproxy_settings')
881
+ current = settings.show_settings
882
+ settings.update_settings('haproxy' => { 'general' => { 'tuning' => { 'maxconn' => '2000' } } })
883
+
884
+ # CRUD via explicit paths (for resources not in registry)
885
+ res = OpnApi::Resource.new(
886
+ client: client,
887
+ base_path: 'firewall/alias',
888
+ search_action: 'search_item',
889
+ crud_action: '%{action}_item',
890
+ )
891
+ all_aliases = res.search
892
+
893
+ # Legacy mode (module/controller/type) is also supported
894
+ res = OpnApi::Resource.new(
895
+ client: client,
896
+ module_name: 'firewall',
897
+ controller: 'alias',
898
+ resource_type: 'item',
899
+ )
900
+
901
+ # UUID resolution for ModelRelationField references
902
+ relation_fields = {
903
+ 'linkedServers' => {
904
+ endpoint: 'haproxy/settings/search_servers',
905
+ multiple: true,
906
+ },
907
+ 'sslCA' => {
908
+ endpoint: 'trust/ca/search',
909
+ id_field: 'refid',
910
+ name_field: 'descr',
911
+ },
912
+ }
913
+ config_with_names = OpnApi::IdResolver.translate_to_names(
914
+ client, 'opnsense01', relation_fields, config_hash,
915
+ )
916
+ config_with_uuids = OpnApi::IdResolver.translate_to_uuids(
917
+ client, 'opnsense01', relation_fields, config_hash,
918
+ )
919
+
920
+ # Service reconfigure orchestration
921
+ OpnApi::ServiceReconfigure.load_defaults!
922
+ OpnApi::ServiceReconfigure[:haproxy].mark('opnsense01', client)
923
+ results = OpnApi::ServiceReconfigure[:haproxy].run
924
+ # => { 'opnsense01' => :ok }
925
+ ```
926
+
927
+ ## Features
928
+
929
+ ### Config loader
930
+
931
+ `OpnApi::Config` loads device credentials from YAML files with a hierarchical search path. Supports multiple devices and is compatible with puppet-opn's device file format.
932
+
933
+ ### ID resolver
934
+
935
+ `OpnApi::IdResolver` translates between UUIDs/IDs and human-readable names for OPNsense ModelRelationField and CertificateField references. Features include:
936
+
937
+ - Per-run caching to minimize API calls
938
+ - Automatic cache refresh on miss (retry once)
939
+ - Support for dot-path field names (e.g. `general.stats.allowedUsers`)
940
+ - Custom `id_field` and `name_field` per relation (e.g. `refid`/`descr` for certificates)
941
+ - Multiple values (comma-separated) and single-value fields
942
+
943
+ ### Normalize
944
+
945
+ `OpnApi::Normalize` handles OPNsense selection-hash normalization. OPNsense returns multi-select fields as hashes with `value`/`selected` keys. This module collapses them to comma-separated strings of selected keys, recursing into nested structures.
946
+
947
+ ### Resource CRUD
948
+
949
+ `OpnApi::Resource` provides a generic CRUD wrapper for OPNsense API endpoints. It supports three resource patterns:
950
+
951
+ - **Standard CRUD**: search/get/add/set/del with UUID
952
+ - **Singleton settings**: GET get / POST set, no UUID, no search/add/del
953
+ - **GET-based search**: Resources using GET instead of POST for search (e.g. snapshots, trust_crl)
954
+
955
+ ### Resource registry
956
+
957
+ `OpnApi::ResourceRegistry` maps user-friendly resource names to exact API endpoint paths. OPNsense has no consistent endpoint naming — some use snake_case with plural search (`search_servers`/`add_server`), others use camelCase (`searchConnection`/`addConnection`), bare names (`search`/`add`), or no separator (`searchroute`/`addroute`). The registry abstracts these differences so users and code don't need to know the implementation details.
958
+
959
+ Resource names follow the [puppet-opn](https://github.com/markt-de/puppet-opn) naming convention (`opn_` prefix stripped).
960
+
961
+ ### Service reconfigure
962
+
963
+ `OpnApi::ServiceReconfigure` orchestrates service reloads after configuration changes. Features include:
964
+
965
+ - Registry pattern with 18 pre-registered OPNsense service groups
966
+ - Mark/run pattern: track devices with changes, then batch-reconfigure
967
+ - Configtest support (e.g. HAProxy validates config before reconfigure)
968
+ - Error tracking: skip reconfigure for devices with failed resource changes
969
+ - Custom group registration for plugins or custom services
970
+
971
+ ## Development
972
+
973
+ ### Contributing
974
+
975
+ Please use the GitHub issues functionality to report any bugs or requests for new features. Feel free to fork and submit pull requests for potential contributions.
976
+
977
+ ## License
978
+
979
+ BSD-2-Clause