|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# |
| 3 | +# Copyright (C) 2018 VyOS maintainers and contributors |
| 4 | +# |
| 5 | +# This program is free software; you can redistribute it and/or modify |
| 6 | +# it under the terms of the GNU General Public License version 2 or later as |
| 7 | +# published by the Free Software Foundation. |
| 8 | +# |
| 9 | +# This program is distributed in the hope that it will be useful, |
| 10 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +# GNU General Public License for more details. |
| 13 | +# |
| 14 | +# You should have received a copy of the GNU General Public License |
| 15 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | +# |
| 17 | +# |
| 18 | + |
| 19 | +import sys |
| 20 | +import os |
| 21 | +import pwd |
| 22 | +import socket |
| 23 | +import urllib3 |
| 24 | + |
| 25 | +import jinja2 |
| 26 | + |
| 27 | +from vyos.config import Config |
| 28 | +from vyos import ConfigError |
| 29 | + |
| 30 | +config_file = r'/etc/salt/minion' |
| 31 | + |
| 32 | +# Please be careful if you edit the template. |
| 33 | +config_tmpl = """ |
| 34 | +### Autogenerated by salt-minion.py ### |
| 35 | +
|
| 36 | +##### Primary configuration settings ##### |
| 37 | +########################################## |
| 38 | +
|
| 39 | +# The hash_type is the hash to use when discovering the hash of a file on |
| 40 | +# the master server. The default is sha256, but md5, sha1, sha224, sha384 and |
| 41 | +# sha512 are also supported. |
| 42 | +# |
| 43 | +# WARNING: While md5 and sha1 are also supported, do not use them due to the |
| 44 | +# high chance of possible collisions and thus security breach. |
| 45 | +# |
| 46 | +# Prior to changing this value, the master should be stopped and all Salt |
| 47 | +# caches should be cleared. |
| 48 | +hash_type: {{ hash_type }} |
| 49 | +
|
| 50 | +##### Logging settings ##### |
| 51 | +########################################## |
| 52 | +# The location of the minion log file |
| 53 | +# The minion log can be sent to a regular file, local path name, or network |
| 54 | +# location. Remote logging works best when configured to use rsyslogd(8) (e.g.: |
| 55 | +# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI |
| 56 | +# format is: <file|udp|tcp>://<host|socketpath>:<port-if-required>/<log-facility> |
| 57 | +#log_file: /var/log/salt/minion |
| 58 | +#log_file: file:///dev/log |
| 59 | +#log_file: udp://loghost:10514 |
| 60 | +# |
| 61 | +log_file: {{ log_file }} |
| 62 | +
|
| 63 | +# The level of messages to send to the console. |
| 64 | +# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. |
| 65 | +# |
| 66 | +# The following log levels are considered INSECURE and may log sensitive data: |
| 67 | +# ['garbage', 'trace', 'debug'] |
| 68 | +# |
| 69 | +# Default: 'warning' |
| 70 | +log_level: {{ log_level }} |
| 71 | +
|
| 72 | +# Set the location of the salt master server, if the master server cannot be |
| 73 | +# resolved, then the minion will fail to start. |
| 74 | +master: |
| 75 | +{% for host in master -%} |
| 76 | +- {{ host }} |
| 77 | +{% endfor %} |
| 78 | +
|
| 79 | +# The user to run salt |
| 80 | +user: {{ user }} |
| 81 | +
|
| 82 | +# The directory to store the pki information in |
| 83 | +pki_dir: /config/salt/pki/minion |
| 84 | +
|
| 85 | +# Explicitly declare the id for this minion to use, if left commented the id |
| 86 | +# will be the hostname as returned by the python call: socket.getfqdn() |
| 87 | +# Since salt uses detached ids it is possible to run multiple minions on the |
| 88 | +# same machine but with different ids, this can be useful for salt compute |
| 89 | +# clusters. |
| 90 | +id: {{ salt_id }} |
| 91 | +
|
| 92 | +
|
| 93 | +# The number of minutes between mine updates. |
| 94 | +mine_interval: {{ mine_interval }} |
| 95 | +
|
| 96 | +verify_master_pubkey_sign: {{ verify_master_pubkey_sign }} |
| 97 | +""" |
| 98 | + |
| 99 | +default_config_data = { |
| 100 | + 'hash_type': 'sha256', |
| 101 | + 'log_file': '/var/log/salt/minion', |
| 102 | + 'log_level': 'warning', |
| 103 | + 'master' : 'salt', |
| 104 | + 'user': 'minion', |
| 105 | + 'salt_id': socket.gethostname(), |
| 106 | + 'mine_interval': '60', |
| 107 | + 'verify_master_pubkey_sign': 'false' |
| 108 | +} |
| 109 | + |
| 110 | +def get_config(): |
| 111 | + salt = default_config_data |
| 112 | + conf = Config() |
| 113 | + if not conf.exists('service salt-minion'): |
| 114 | + return None |
| 115 | + else: |
| 116 | + conf.set_level('service salt-minion') |
| 117 | + |
| 118 | + if conf.exists('hash_type'): |
| 119 | + salt['hash_type'] = conf.return_value('hash_type') |
| 120 | + |
| 121 | + if conf.exists('log_file'): |
| 122 | + salt['log_file'] = conf.return_value('log_file') |
| 123 | + |
| 124 | + if conf.exists('log_level'): |
| 125 | + salt['log_level'] = conf.return_value('log_level') |
| 126 | + |
| 127 | + if conf.exists('master'): |
| 128 | + master = conf.return_values('master') |
| 129 | + salt['master'] = master |
| 130 | + |
| 131 | + if conf.exists('id'): |
| 132 | + salt['salt_id'] = conf.return_value('id') |
| 133 | + |
| 134 | + if conf.exists('user'): |
| 135 | + salt['user'] = conf.return_value('user') |
| 136 | + |
| 137 | + if conf.exists('mine_interval'): |
| 138 | + salt['mine_interval'] = conf.return_value('mine_interval') |
| 139 | + |
| 140 | + salt['master-key'] = None |
| 141 | + if conf.exists('master-key'): |
| 142 | + salt['master-key'] = conf.return_value('master-key') |
| 143 | + salt['verify_master_pubkey_sign'] = 'true' |
| 144 | + |
| 145 | + return salt |
| 146 | + |
| 147 | +def generate(salt): |
| 148 | + paths = ['/etc/salt/','/var/run/salt','/opt/vyatta/etc/config/salt/'] |
| 149 | + directory = '/opt/vyatta/etc/config/salt/pki/minion' |
| 150 | + uid = pwd.getpwnam(salt['user']).pw_uid |
| 151 | + http = urllib3.PoolManager() |
| 152 | + |
| 153 | + if salt is None: |
| 154 | + return None |
| 155 | + |
| 156 | + if not os.path.exists(directory): |
| 157 | + os.makedirs(directory) |
| 158 | + |
| 159 | + tmpl = jinja2.Template(config_tmpl) |
| 160 | + config_text = tmpl.render(salt) |
| 161 | + with open(config_file, 'w') as f: |
| 162 | + f.write(config_text) |
| 163 | + path = "/etc/salt/" |
| 164 | + for path in paths: |
| 165 | + for root, dirs, files in os.walk(path): |
| 166 | + for usgr in dirs: |
| 167 | + os.chown(os.path.join(root, usgr), uid, 100) |
| 168 | + for usgr in files: |
| 169 | + os.chown(os.path.join(root, usgr), uid, 100) |
| 170 | + |
| 171 | + if not os.path.exists('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub'): |
| 172 | + if not salt['master-key'] is None: |
| 173 | + r = http.request('GET', salt['master-key'], preload_content=False) |
| 174 | + |
| 175 | + with open('/opt/vyatta/etc/config/salt/pki/minion/master_sign.pub', 'wb') as out: |
| 176 | + while True: |
| 177 | + data = r.read(1024) |
| 178 | + if not data: |
| 179 | + break |
| 180 | + out.write(data) |
| 181 | + |
| 182 | + r.release_conn() |
| 183 | + |
| 184 | + return None |
| 185 | + |
| 186 | +def apply(salt): |
| 187 | + if salt is not None: |
| 188 | + os.system("sudo systemctl restart salt-minion") |
| 189 | + else: |
| 190 | + # Salt access is removed in the commit |
| 191 | + os.system("sudo systemctl stop salt-minion") |
| 192 | + os.unlink(config_file) |
| 193 | + |
| 194 | + return None |
| 195 | + |
| 196 | +if __name__ == '__main__': |
| 197 | + try: |
| 198 | + c = get_config() |
| 199 | + generate(c) |
| 200 | + apply(c) |
| 201 | + except ConfigError as e: |
| 202 | + print(e) |
| 203 | + sys.exit(1) |
0 commit comments