1
0

best_practices.rst 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. .. _best-practices:
  2. ============================
  3. Salt :index:`Best Practices`
  4. ============================
  5. Salt's extreme flexibility leads to many questions concerning the structure of
  6. configuration files.
  7. This document exists to clarify these points through examples and
  8. code.
  9. General rules
  10. -------------
  11. 1. Modularity and clarity should be emphasized whenever possible.
  12. 2. Create clear relations between pillars and states.
  13. 3. Use variables when it makes sense but don't overuse them.
  14. 4. Store sensitive data in pillar.
  15. 5. Don't use grains for matching in your pillar top file for any sensitive
  16. pillars.
  17. Structuring States and Formulas
  18. -------------------------------
  19. When structuring Salt States and Formulas it is important to begin with the
  20. directory structure. A proper directory structure clearly defines the
  21. functionality of each state to the user via visual inspection of the state's
  22. name.
  23. Reviewing the :formula_url:`MySQL Salt Formula <mysql-formula>`
  24. it is clear to see the benefits to the end-user when reviewing a sample of the
  25. available states:
  26. .. code-block:: bash
  27. /srv/salt/mysql/files/
  28. /srv/salt/mysql/client.sls
  29. /srv/salt/mysql/map.jinja
  30. /srv/salt/mysql/python.sls
  31. /srv/salt/mysql/server.sls
  32. This directory structure would lead to these states being referenced in a top
  33. file in the following way:
  34. .. code-block:: yaml
  35. base:
  36. 'web*':
  37. - mysql.client
  38. - mysql.python
  39. 'db*':
  40. - mysql.server
  41. This clear definition ensures that the user is properly informed of what each
  42. state will do.
  43. Another example comes from the :formula_url:`vim-formula`:
  44. .. code-block:: bash
  45. /srv/salt/vim/files/
  46. /srv/salt/vim/absent.sls
  47. /srv/salt/vim/init.sls
  48. /srv/salt/vim/map.jinja
  49. /srv/salt/vim/nerdtree.sls
  50. /srv/salt/vim/pyflakes.sls
  51. /srv/salt/vim/salt.sls
  52. Once again viewing how this would look in a top file:
  53. /srv/salt/top.sls:
  54. .. code-block:: yaml
  55. base:
  56. 'web*':
  57. - vim
  58. - vim.nerdtree
  59. - vim.pyflakes
  60. - vim.salt
  61. 'db*':
  62. - vim.absent
  63. The usage of a clear top-level directory as well as properly named states
  64. reduces the overall complexity and leads a user to both understand what will
  65. be included at a glance and where it is located.
  66. In addition :ref:`Formulas <conventions-formula>` should
  67. be used as often as possible.
  68. .. note::
  69. Formulas repositories on the saltstack-formulas GitHub organization should
  70. not be pointed to directly from systems that automatically fetch new
  71. updates such as GitFS or similar tooling. Instead formulas repositories
  72. should be forked on GitHub or cloned locally, where unintended, automatic
  73. changes will not take place.
  74. Structuring Pillar Files
  75. ------------------------
  76. :ref:`Pillars <pillar>` are used to store
  77. secure and insecure data pertaining to minions. When designing the structure
  78. of the ``/srv/pillar`` directory, the pillars contained within
  79. should once again be focused on clear and concise data which users can easily
  80. review, modify, and understand.
  81. The ``/srv/pillar/`` directory is primarily controlled by ``top.sls``. It
  82. should be noted that the pillar ``top.sls`` is not used as a location to
  83. declare variables and their values. The ``top.sls`` is used as a way to
  84. include other pillar files and organize the way they are matched based on
  85. environments or grains.
  86. An example ``top.sls`` may be as simple as the following:
  87. /srv/pillar/top.sls:
  88. .. code-block:: yaml
  89. base:
  90. '*':
  91. - packages
  92. Any number of matchers can be added to the base environment. For example, here
  93. is an expanded version of the Pillar top file stated above:
  94. /srv/pillar/top.sls:
  95. .. code-block:: yaml
  96. base:
  97. '*':
  98. - packages
  99. 'web*':
  100. - apache
  101. - vim
  102. Or an even more complicated example, using a variety of matchers in numerous
  103. environments:
  104. /srv/pillar/top.sls:
  105. .. code-block:: yaml
  106. base:
  107. '*':
  108. - apache
  109. dev:
  110. 'os:Debian':
  111. - match: grain
  112. - vim
  113. test:
  114. '* and not G@os: Debian':
  115. - match: compound
  116. - emacs
  117. It is clear to see through these examples how the top file provides users with
  118. power but when used incorrectly it can lead to confusing configurations. This
  119. is why it is important to understand that the top file for pillar is not used
  120. for variable definitions.
  121. Each SLS file within the ``/srv/pillar/`` directory should correspond to the
  122. states which it matches.
  123. This would mean that the ``apache`` pillar file should contain data relevant to
  124. Apache. Structuring files in this way once again ensures modularity, and
  125. creates a consistent understanding throughout our Salt environment. Users can
  126. expect that pillar variables found in an Apache state will live inside of an
  127. Apache pillar:
  128. ``/srv/pillar/apache.sls``:
  129. .. code-block:: yaml
  130. apache:
  131. lookup:
  132. name: httpd
  133. config:
  134. tmpl: /etc/httpd/httpd.conf
  135. While this pillar file is simple, it shows how a pillar file explicitly
  136. relates to the state it is associated with.
  137. Variable Flexibility
  138. --------------------
  139. Salt allows users to define variables in SLS files. When creating a state
  140. variables should provide users with as much flexibility as possible. This
  141. means that variables should be clearly defined and easy to manipulate, and
  142. that sane defaults should exist in the event a variable is not properly
  143. defined. Looking at several examples shows how these different items can
  144. lead to extensive flexibility.
  145. Although it is possible to set variables locally, this is generally not
  146. preferred:
  147. ``/srv/salt/apache/conf.sls``:
  148. .. code-block:: jinja
  149. {% set name = 'httpd' %}
  150. {% set tmpl = 'salt://apache/files/httpd.conf' %}
  151. include:
  152. - apache
  153. apache_conf:
  154. file.managed:
  155. - name: {{ name }}
  156. - source: {{ tmpl }}
  157. - template: jinja
  158. - user: root
  159. - watch_in:
  160. - service: apache
  161. When generating this information it can be easily transitioned to the pillar
  162. where data can be overwritten, modified, and applied to multiple states, or
  163. locations within a single state:
  164. ``/srv/pillar/apache.sls``:
  165. .. code-block:: yaml
  166. apache:
  167. lookup:
  168. name: httpd
  169. config:
  170. tmpl: salt://apache/files/httpd.conf
  171. ``/srv/salt/apache/conf.sls``:
  172. .. code-block:: jinja
  173. {% from "apache/map.jinja" import apache with context %}
  174. include:
  175. - apache
  176. apache_conf:
  177. file.managed:
  178. - name: {{ salt['pillar.get']('apache:lookup:name') }}
  179. - source: {{ salt['pillar.get']('apache:lookup:config:tmpl') }}
  180. - template: jinja
  181. - user: root
  182. - watch_in:
  183. - service: apache
  184. This flexibility provides users with a centralized location to modify
  185. variables, which is extremely important as an environment grows.
  186. Modularity Within States
  187. ------------------------
  188. Ensuring that states are modular is one of the key concepts to understand
  189. within Salt. When creating a state a user must consider how many times the
  190. state could be re-used, and what it relies on to operate. Below are several
  191. examples which will iteratively explain how a user can go from a state which
  192. is not very modular to one that is:
  193. ``/srv/salt/apache/init.sls``:
  194. .. code-block:: yaml
  195. httpd:
  196. pkg:
  197. - installed
  198. service.running:
  199. - enable: True
  200. /etc/httpd/httpd.conf:
  201. file.managed:
  202. - source: salt://apache/files/httpd.conf
  203. - template: jinja
  204. - watch_in:
  205. - service: httpd
  206. The example above is probably the worst-case scenario when writing a state.
  207. There is a clear lack of focus by naming both the pkg/service, and managed
  208. file directly as the state ID. This would lead to changing multiple requires
  209. within this state, as well as others that may depend upon the state.
  210. Imagine if a require was used for the ``httpd`` package in another state, and
  211. then suddenly it's a custom package. Now changes need to be made in multiple
  212. locations which increases the complexity and leads to a more error prone
  213. configuration.
  214. There is also the issue of having the configuration file located in the init,
  215. as a user would be unable to simply install the service and use the default
  216. conf file.
  217. Our second revision begins to address the referencing by using ``- name``, as
  218. opposed to direct ID references:
  219. ``/srv/salt/apache/init.sls``:
  220. .. code-block:: yaml
  221. apache:
  222. pkg.installed:
  223. - name: httpd
  224. service.running:
  225. - name: httpd
  226. - enable: True
  227. apache_conf:
  228. file.managed:
  229. - name: /etc/httpd/httpd.conf
  230. - source: salt://apache/files/httpd.conf
  231. - template: jinja
  232. - watch_in:
  233. - service: apache
  234. The above init file is better than our original, yet it has several issues
  235. which lead to a lack of modularity. The first of these problems is the usage
  236. of static values for items such as the name of the service, the name of the
  237. managed file, and the source of the managed file. When these items are hard
  238. coded they become difficult to modify and the opportunity to make mistakes
  239. arises. It also leads to multiple edits that need to occur when changing
  240. these items (imagine if there were dozens of these occurrences throughout the
  241. state!). There is also still the concern of the configuration file data living
  242. in the same state as the service and package.
  243. In the next example steps will be taken to begin addressing these issues.
  244. Starting with the addition of a map.jinja file (as noted in the
  245. :ref:`Formula documentation <conventions-formula>`), and
  246. modification of static values:
  247. ``/srv/salt/apache/map.jinja``:
  248. .. code-block:: jinja
  249. {% set apache = salt['grains.filter_by']({
  250. 'Debian': {
  251. 'server': 'apache2',
  252. 'service': 'apache2',
  253. 'conf': '/etc/apache2/apache.conf',
  254. },
  255. 'RedHat': {
  256. 'server': 'httpd',
  257. 'service': 'httpd',
  258. 'conf': '/etc/httpd/httpd.conf',
  259. },
  260. }, merge=salt['pillar.get']('apache:lookup')) %}
  261. /srv/pillar/apache.sls:
  262. .. code-block:: yaml
  263. apache:
  264. lookup:
  265. config:
  266. tmpl: salt://apache/files/httpd.conf
  267. ``/srv/salt/apache/init.sls``:
  268. .. code-block:: jinja
  269. {% from "apache/map.jinja" import apache with context %}
  270. apache:
  271. pkg.installed:
  272. - name: {{ apache.server }}
  273. service.running:
  274. - name: {{ apache.service }}
  275. - enable: True
  276. apache_conf:
  277. file.managed:
  278. - name: {{ apache.conf }}
  279. - source: {{ salt['pillar.get']('apache:lookup:config:tmpl') }}
  280. - template: jinja
  281. - user: root
  282. - watch_in:
  283. - service: apache
  284. The changes to this state now allow us to easily identify the location of the
  285. variables, as well as ensuring they are flexible and easy to modify.
  286. While this takes another step in the right direction, it is not yet complete.
  287. Suppose the user did not want to use the provided conf file, or even their own
  288. configuration file, but the default apache conf. With the current state setup
  289. this is not possible. To attain this level of modularity this state will need
  290. to be broken into two states.
  291. ``/srv/salt/apache/map.jinja``:
  292. .. code-block:: jinja
  293. {% set apache = salt['grains.filter_by']({
  294. 'Debian': {
  295. 'server': 'apache2',
  296. 'service': 'apache2',
  297. 'conf': '/etc/apache2/apache.conf',
  298. },
  299. 'RedHat': {
  300. 'server': 'httpd',
  301. 'service': 'httpd',
  302. 'conf': '/etc/httpd/httpd.conf',
  303. },
  304. }, merge=salt['pillar.get']('apache:lookup')) %}
  305. ``/srv/pillar/apache.sls``:
  306. .. code-block:: yaml
  307. apache:
  308. lookup:
  309. config:
  310. tmpl: salt://apache/files/httpd.conf
  311. ``/srv/salt/apache/init.sls``:
  312. .. code-block:: jinja
  313. {% from "apache/map.jinja" import apache with context %}
  314. apache:
  315. pkg.installed:
  316. - name: {{ apache.server }}
  317. service.running:
  318. - name: {{ apache.service }}
  319. - enable: True
  320. ``/srv/salt/apache/conf.sls``:
  321. .. code-block:: jinja
  322. {% from "apache/map.jinja" import apache with context %}
  323. include:
  324. - apache
  325. apache_conf:
  326. file.managed:
  327. - name: {{ apache.conf }}
  328. - source: {{ salt['pillar.get']('apache:lookup:config:tmpl') }}
  329. - template: jinja
  330. - user: root
  331. - watch_in:
  332. - service: apache
  333. This new structure now allows users to choose whether they only wish to
  334. install the default Apache, or if they wish, overwrite the default package,
  335. service, configuration file location, or the configuration file itself. In
  336. addition to this the data has been broken between multiple files allowing for
  337. users to identify where they need to change the associated data.
  338. Storing Secure Data
  339. -------------------
  340. Secure data refers to any information that you would not wish to share with
  341. anyone accessing a server. This could include data such as passwords,
  342. keys, or other information.
  343. As all data within a state is accessible by EVERY server that is connected
  344. it is important to store secure data within pillar. This will ensure that only
  345. those servers which require this secure data have access to it. In this
  346. example a use can go from an insecure configuration to one which is only
  347. accessible by the appropriate hosts:
  348. ``/srv/salt/mysql/testerdb.sls``:
  349. .. code-block:: yaml
  350. testdb:
  351. mysql_database.present:
  352. - name: testerdb
  353. ``/srv/salt/mysql/user.sls``:
  354. .. code-block:: yaml
  355. include:
  356. - mysql.testerdb
  357. testdb_user:
  358. mysql_user.present:
  359. - name: frank
  360. - password: "test3rdb"
  361. - host: localhost
  362. - require:
  363. - sls: mysql.testerdb
  364. Many users would review this state and see that the password is there in plain
  365. text, which is quite problematic. It results in several issues which may not
  366. be immediately visible.
  367. The first of these issues is clear to most users -- the password being visible
  368. in this state. This means that any minion will have a copy of this, and
  369. therefore the password which is a major security concern as minions may not
  370. be locked down as tightly as the master server.
  371. The other issue that can be encountered is access by users on the master. If
  372. everyone has access to the states (or their repository), then they are able to
  373. review this password. Keeping your password data accessible by only a few
  374. users is critical for both security and peace of mind.
  375. There is also the issue of portability. When a state is configured this way
  376. it results in multiple changes needing to be made. This was discussed in the
  377. sections above but it is a critical idea to drive home. If states are not
  378. portable it may result in more work later!
  379. Fixing this issue is relatively simple, the content just needs to be moved to
  380. the associated pillar:
  381. ``/srv/pillar/mysql.sls``:
  382. .. code-block:: yaml
  383. mysql:
  384. lookup:
  385. name: testerdb
  386. password: test3rdb
  387. user: frank
  388. host: localhost
  389. ``/srv/salt/mysql/testerdb.sls``:
  390. .. code-block:: jinja
  391. testdb:
  392. mysql_database.present:
  393. - name: {{ salt['pillar.get']('mysql:lookup:name') }}
  394. ``/srv/salt/mysql/user.sls``:
  395. .. code-block:: jinja
  396. include:
  397. - mysql.testerdb
  398. testdb_user:
  399. mysql_user.present:
  400. - name: {{ salt['pillar.get']('mysql:lookup:user') }}
  401. - password: {{ salt['pillar.get']('mysql:lookup:password') }}
  402. - host: {{ salt['pillar.get']('mysql:lookup:host') }}
  403. - require:
  404. - sls: mysql.testerdb
  405. Now that the database details have been moved to the associated pillar file,
  406. only machines which are targeted via pillar will have access to these details.
  407. Access to users who should not be able to review these details can also be
  408. prevented while ensuring that they are still able to write states which take
  409. advantage of this information.