aboutsummaryrefslogtreecommitdiff
path: root/content/blog/cfengine/leveraging-yaml.md
blob: e77332562f1b86421932453f8933e9dce07aef14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
---
title: "Leveraging yaml with cfengine"
date: 2018-09-25
description: How to leverage yaml inventory files with cfengine
tags:
  - cfengine
---

## Introduction

CFEngine has core support for JSON and YAML. You can use this support to read, access, and merge JSON and YAML files and use these to keep policy files internal and simple. You
access the data using the usual cfengine standard primitives.

The use case bellow lacks a bit or error control with argument validation, it will fail miserably if the YAML file is invalid.

## Example yaml

In `cmdb/hosts/andromeda.yaml` we describe some properties of a host named andromeda:

{{< highlight yaml >}}
domain: adyxax.org
host_interface: dummy0
host_ip: "10.1.0.255"

tunnels:
    collab:
        port: 1195
        ip: "10.1.0.15"
        peer: "10.1.0.14"
        remote_host: collab.example.net
        remote_port: 1199
    legend:
        port: 1194
        ip: "10.1.0.3"
        peer: "10.1.0.2"
        remote_host: legend.adyxax.org
        remote_port: 1195
{{< /highlight >}}

## Reading the yaml

I am bundling the values in a common bundle, accessible globally. This is one of the first bundles processed in the order my policy files are loaded. This is just an extract, you can load multiple files and merge them to distribute common
settings :
{{< highlight yaml >}}
bundle common g
{
    vars:
        has_host_data::
            "host_data" data => readyaml("$(sys.inputdir)/cmdb/hosts/$(sys.host).yaml", 100k);
    classes:
        any::
            "has_host_data" expression => fileexists("$(sys.inputdir)/cmdb/hosts/$(sys.host).yaml");
}
{{< /highlight >}}

## Using the data

### Cfengine agent bundle

We access the data using the global g.host_data variable, here is a complete example :
{{< highlight yaml >}}
bundle agent openvpn
{
    vars:
        any::
            "tunnels" slist => getindices("g.host_data[tunnels]");
    files:
        any::
            "/etc/openvpn/common.key"
                create => "true",
                edit_defaults => empty,
                perms => system_owned("440"),
                copy_from => local_dcp("$(sys.inputdir)/templates/openvpn/common.key.cftpl"),
                classes => if_repaired("openvpn_common_key_repaired");
    methods:
        any::
            "any" usebundle => install_package("$(this.bundle)", "openvpn");
            "any" usebundle => openvpn_tunnel("$(tunnels)");
    services:
        linux::
            "openvpn@$(tunnels)"
                service_policy => "start",
                classes => if_repaired("tunnel_$(tunnels)_service_repaired");
    commands:
        any::
            "/usr/sbin/service openvpn@$(tunnels) restart"
                classes => if_repaired("tunnel_$(tunnels)_service_repaired"),
                ifvarclass => "openvpn_common_key_repaired";
    reports:
        any::
            "$(this.bundle): common.key repaired" ifvarclass => "openvpn_common_key_repaired";
            "$(this.bundle): $(tunnels) service repaired" ifvarclass => "tunnel_$(tunnels)_service_repaired";
}
 
bundle agent openvpn_tunnel(tunnel)
{
    classes:
        any::
            "has_remote" and => { isvariable("g.host_data[tunnels][$(tunnel)][remote_host]"),
                                  isvariable("g.host_data[tunnels][$(tunnel)][remote_port]") };
    files:
        any::
            "/etc/openvpn/$(tunnel).conf"
                create => "true",
                edit_defaults => empty,
                perms => system_owned("440"),
                edit_template => "$(sys.inputdir)/templates/openvpn/tunnel.conf.cftpl",
                template_method => "cfengine",
                classes => if_repaired("openvpn_$(tunnel)_conf_repaired");
    commands:
        any::
            "/usr/sbin/service openvpn@$(tunnel) restart"
                classes => if_repaired("tunnel_$(tunnel)_service_repaired"),
                ifvarclass => "openvpn_$(tunnel)_conf_repaired";
    reports:
        any::
            "$(this.bundle): $(tunnel).conf repaired" ifvarclass => "openvpn_$(tunnel)_conf_repaired";
            "$(this.bundle): $(tunnel) service repaired" ifvarclass => "tunnel_$(tunnel)_service_repaired";
}
{{< /highlight >}}

### Template file

Templates can reference the g.host_data too, like in the following :
{{< highlight cfg >}}
[%CFEngine BEGIN %]
proto udp
port $(g.host_data[tunnels][$(openvpn_tunnel.tunnel)][port])
dev-type tun
dev tun_$(openvpn_tunnel.tunnel)
comp-lzo
script-security 2

ping 10
ping-restart 20
ping-timer-rem
persist-tun
persist-key

cipher AES-128-CBC

secret /etc/openvpn/common.key
ifconfig $(g.host_data[tunnels][$(openvpn_tunnel.tunnel)][ip]) $(g.host_data[tunnels][$(openvpn_tunnel.tunnel)][peer])

user nobody
[%CFEngine centos:: %]
group nobody
[%CFEngine ubuntu:: %]
group nogroup

[%CFEngine has_remote:: %]
remote $(g.host_data[tunnels][$(openvpn_tunnel.tunnel)][remote_host]) $(g.host_data[tunnels][$(openvpn_tunnel.tunnel)][remote_port])

[%CFEngine END %]
{{< /highlight >}}

## References
- https://docs.cfengine.com/docs/master/examples-tutorials-json-yaml-support-in-cfengine.html
- https://docs.cfengine.com/docs/3.10/reference-functions-readyaml.html
- https://docs.cfengine.com/docs/3.10/reference-functions-mergedata.html