Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS and useip parameters added #40

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/resources/host.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ description: |-
- **proxyid** (String) ID of proxy to monitor this host
- **tag** (Block Set) (see [below for nested schema](#nestedblock--tag))
- **templates** (Set of String) Template IDs to attach to this host
- **tls_connect** (String) Determines how connections to the host are encrypted. Possible values are: 'none' - No encryption, 'psk' - Pre-Shared Key (PSK), 'cert' - Certificate-based encryption
- **tls_accept** (String) Determines how connections from the host are encrypted. Possible values are: 'none' - No encryption, 'psk' - Pre-Shared Key (PSK), 'cert' - Certificate-based encryption.
- **tls_issuer** (String) The issuer of the TLS certificate used for encryption, if applicable.
- **tls_subject** (String) The subject of the TLS certificate used for encryption, if applicable.
- **tls_psk_identity** (String) The identity associated with the Pre-Shared Key (PSK) used for encryption. Required if either tls_connect or tls_accept has PSK enabled.
- **tls_psk** (String) The Pre-Shared Key (PSK) used for encryption. Required if either tls_connect or tls_accept has PSK enabled. Must be at least 32 hexadecimal characters.


<a id="nestedblock--interface"></a>
### Nested Schema for `interface`
Expand All @@ -40,6 +47,7 @@ Optional:

- **dns** (String) Interface DNS name
- **ip** (String) Interface IP address
- **useip** (Boolean) Whether to use the IP address or DNS name for connecting to the host
- **main** (Boolean) Primary interface of this type
- **port** (Number) Destination Port
- **snmp3_authpassphrase** (String) Authentication Passphrase (v3 only)
Expand Down
144 changes: 134 additions & 10 deletions provider/resource_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,21 @@ var hostSchemaBase = map[string]*schema.Schema{
ValidateFunc: validation.StringIsNotWhiteSpace,
Default: "{$SNMP3_SECURITYNAME}",
},
"useip": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether to use IP address. True to use IP, false to use DNS name.",
},
},

// Ensure dns is set when useip is set to false
CustomizeDiff: func(d *schema.ResourceDiff, meta interface{}) error {
if useIP, ok := d.GetOk("useip"); ok && !useIP.(bool) {
d.SetNewComputed("dns")
d.ForceNew("dns")
}
return nil
},
},
},
Expand Down Expand Up @@ -368,6 +383,99 @@ var hostSchemaBase = map[string]*schema.Schema{
},
},
},
"tls_connect": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "none",
Description: "Connections to host. Possible values are: 'none' - No encryption, 'psk' - PSK, 'cert' - certificate.",
ValidateFunc: validation.StringInSlice([]string{"none", "psk", "cert"}, false),
},
"tls_accept": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "none",
Description: "Connections from host. Possible values are: 'none' - No encryption, 'psk' - PSK, 'cert' - certificate.",
ValidateFunc: validation.StringInSlice([]string{"none", "psk", "cert"}, false),
},
"tls_issuer": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Certificate issuer.",
},
"tls_subject": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Certificate subject.",
},
"tls_psk_identity": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Description: "PSK identity. Required if either tls_connect or tls_accept has PSK enabled.",
},
"tls_psk": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Pre-shared key. Required if either tls_connect or tls_accept has PSK enabled.",
Sensitive: true,
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
value := val.(string)
hexRegex := regexp.MustCompile(`^[0-9a-fA-F]{32,}$`)
if !hexRegex.MatchString(value) {
errs = append(errs, fmt.Errorf("tls_psk must be at least 32 hexadecimal characters"))
}
return warns, errs
},
},
}

// convertTLSBitmapToString converts TLS integer values into friendly strings
func convertTLSBitmapToString(bitmap string) string {
switch bitmap {
case "1":
return "none"
case "2":
return "psk"
case "4":
return "cert"
default:
return "unknown"
}
}

// convertTLSStringToBitmap converts TLS friendly string values to integer values
func convertTLSStringToBitmap(value string) string {
switch value {
case "none":
return "1"
case "psk":
return "2"
case "cert":
return "4"
default:
return "unknown"
}
}

// customValidatePSKFields ensures TLS PSK fields are provided when PSK encryption is configured.
func customValidatePSKFields(diff *schema.ResourceDiff, v interface{}) error {
tlsConnect, tlsConnectOk := diff.GetOk("tls_connect")
tlsAccept, tlsAcceptOk := diff.GetOk("tls_accept")

// Validate only if tls_connect or tls_accept is set to "psk"
if (tlsConnectOk && tlsConnect.(string) == "psk") || (tlsAcceptOk && tlsAccept.(string) == "psk") {
_, pskIdentityOk := diff.GetOk("tls_psk_identity")
_, pskOk := diff.GetOk("tls_psk")

if !pskIdentityOk {
return fmt.Errorf("tls_psk_identity must be provided when tls_connect or tls_accept is set to 'psk'")
}
if !pskOk {
return fmt.Errorf("tls_psk must be provided when tls_connect or tls_accept is set to 'psk'")
}
}

return nil
}

// resourceHost terraform host resource entrypoint
Expand All @@ -381,6 +489,7 @@ func resourceHost() *schema.Resource {
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
CustomizeDiff: customValidatePSKFields,
}
}

Expand Down Expand Up @@ -451,22 +560,24 @@ func hostGenerateInterfaces(d *schema.ResourceData, m interface{}) (interfaces z
prefix := fmt.Sprintf("interface.%d.", i)
typeId := HOST_IFACE_TYPES[d.Get(prefix+"type").(string)]

useIP := d.Get(prefix + "useip").(bool)
useIPStr := "0" // Default to "1"
if useIP {
useIPStr = "1"
}

interfaces[i] = zabbix.HostInterface{
IP: d.Get(prefix + "ip").(string),
DNS: d.Get(prefix + "dns").(string),
Main: "0",
Type: typeId,
UseIP: "0",
UseIP: useIPStr,
}
if interfaces[i].IP == "" && interfaces[i].DNS == "" {
err = errors.New("interface requires either an IP or DNS entry")
return
}

if interfaces[i].IP != "" {
interfaces[i].UseIP = "1"
}

if d.Get(prefix + "main").(bool) {
interfaces[i].Main = "1"
}
Expand Down Expand Up @@ -542,11 +653,17 @@ func hostGenerateInventory(d *schema.ResourceData) (zabbix.Inventory, error) {
// buildHostObject create host struct
func buildHostObject(d *schema.ResourceData, m interface{}) (*zabbix.Host, error) {
item := zabbix.Host{
Host: d.Get("host").(string),
Name: d.Get("name").(string),
ProxyID: d.Get("proxyid").(string),
InventoryMode: HINV_LOOKUP[d.Get("inventory_mode").(string)],
Status: 0,
Host: d.Get("host").(string),
Name: d.Get("name").(string),
ProxyID: d.Get("proxyid").(string),
InventoryMode: HINV_LOOKUP[d.Get("inventory_mode").(string)],
Status: 0,
TLSConnect: convertTLSStringToBitmap(d.Get("tls_connect").(string)),
TLSAccept: convertTLSStringToBitmap(d.Get("tls_accept").(string)),
TLSIssuer: d.Get("tls_issuer").(string),
TLSSubject: d.Get("tls_subject").(string),
TLSPSKIdentity: d.Get("tls_psk_identity").(string),
TLSPSK: d.Get("tls_psk").(string),
}

if !d.Get("enabled").(bool) {
Expand Down Expand Up @@ -677,6 +794,12 @@ func hostRead(d *schema.ResourceData, m interface{}, params zabbix.Params) error
d.Set("proxyid", host.ProxyID)
d.Set("enabled", host.Status == 0)
d.Set("inventory_mode", HINV_LOOKUP_REV[host.InventoryMode])
d.Set("tls_issuer", host.TLSIssuer)
d.Set("tls_subject", host.TLSSubject)
d.Set("tls_accept", convertTLSBitmapToString(host.TLSAccept))
d.Set("tls_connect", convertTLSBitmapToString(host.TLSConnect))
d.Set("tls_psk_identity", host.TLSPSKIdentity)
d.Set("tls_psk", host.TLSPSK)

d.Set("interface", flattenHostInterfaces(host, d, m))
d.Set("templates", flattenTemplateIds(host.ParentTemplateIDs))
Expand Down Expand Up @@ -713,6 +836,7 @@ func flattenHostInterfaces(host zabbix.Host, d *schema.ResourceData, m interface
"main": host.Interfaces[i].Main == "1",
"port": port,
"type": HOST_IFACE_TYPES_REV[host.Interfaces[i].Type],
"useip": host.Interfaces[i].UseIP,
}

// Set defaults, as these may or may not be bounced back
Expand Down