smart_proxy_salt 4.0.0 → 5.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63743a9b37c7860b60af93db9735f210608221d87675c7d11764eb9f08348caa
4
- data.tar.gz: 38fa3a0d210534f63d1f925790d0a062b20a907499dad0ba2bae1125a0bb8190
3
+ metadata.gz: 4ba18b31812c408bbbef40601e2f7279ee6f04d55a62b999b7b993f0158aede8
4
+ data.tar.gz: 82822b47407c7c5e66151627a7935d294566c6e5dc17c4bbf7c2674890653826
5
5
  SHA512:
6
- metadata.gz: '008b29492b0f227e04ba281423df738e9334e040492f2fcf62172e866cccd931a80de5a5eac14d8e5b4c81a17dd26697e11f89aa696ab28670471f55be585e26'
7
- data.tar.gz: 05a4afde3202a35328d2b2daa4cc2266808a9c7af01b53dae3876a4a8b033ef898fc07e1d86bfc9a22c05b7ab3bcdb924884c34b1d5546e1a8c93a39ec256623
6
+ metadata.gz: d95ddd85199c088db57c6248d99d7a370e36c74191aeda0d74fd4f7fb7d573ee50a0b4a107092d2f8f6c3018c131e6127236cc5818f84c835eecabd0b21ef6bf
7
+ data.tar.gz: d9866d18feed738e03d0e47784ee75b41921b65bcbbd33e88aec5856cfd5160e4b0b5e5485d5c25c3a6c601d6c519bde984599b734f7f081b7adce9de53c3277
@@ -0,0 +1,65 @@
1
+ # /etc/salt/master.d/foreman.config Example configuration
2
+ #
3
+ # This file summarizes configurations for the salt-master. Modify directories and
4
+ # parameters to fit your setup. When you're done, remove the .example from the
5
+ # filename so the salt-master will make use of it.
6
+ # Have a look at the [Foreman Salt Plugin Documentation](https://theforeman.org/plugins/foreman_salt/) for detailed explanations.
7
+ #
8
+ # After editing this file, run the following command to active the changes:
9
+ # $ systemctl restart salt-master
10
+
11
+
12
+ ##
13
+ # Autosign
14
+ autosign_grains_dir: /var/lib/foreman-proxy/salt/grains
15
+ autosign_file: /etc/salt/autosign.conf
16
+ # Uncomment the next line to make use of the autosign host name file (not recommended)
17
+ # permissive_pki_access: True
18
+
19
+
20
+ ##
21
+ # Node classifier
22
+ master_tops:
23
+ ext_nodes: /usr/bin/foreman-node
24
+
25
+
26
+ ##
27
+ # Pillar data access
28
+ ext_pillar:
29
+ - puppet: /usr/bin/foreman-node
30
+
31
+
32
+ ##
33
+ # Salt API access
34
+ external_auth:
35
+ pam:
36
+ saltuser: # Username of your salt user
37
+ - '@runner'
38
+
39
+ rest_cherrypy:
40
+ port: 9191
41
+ ssl_key: /etc/puppet/example.key # Add the path to your Puppet ssl key here
42
+ ssl_crt: /etc/puppet/example.crt # Add the path to your Puppet ssl certificate here
43
+
44
+
45
+ ##
46
+ # Remote execution provider
47
+ publisher_acl:
48
+ foreman-proxy:
49
+ - state.template_str
50
+
51
+
52
+ ##
53
+ # Salt environment (optional)
54
+ file_roots:
55
+ base:
56
+ - /srv/salt
57
+
58
+
59
+ ##
60
+ # Reactors
61
+ reactor:
62
+ - 'salt/auth': # Autosign reactor
63
+ - /var/lib/foreman-proxy/salt/reactors/foreman_minion_auth.sls
64
+ - 'salt/job/*/ret/*': # Report reactor
65
+ - /var/lib/foreman-proxy/salt/reactors/foreman_report_upload.sls
@@ -62,54 +62,42 @@ module Proxy
62
62
  end
63
63
 
64
64
  def append_value_to_file(filepath, value)
65
- result = false
66
- begin
67
- raise "No such file: #{filepath}" unless File.exist?(filepath)
68
-
69
- file = open(filepath, File::RDWR)
70
- found = false
71
- file.each_line { |line| found = true if line.chomp == value }
72
- file.puts value unless found
73
- file.close
74
-
75
- logger.info "Added an entry to '#{filepath}' successfully."
76
- result = true
77
- rescue SystemCallError => e
78
- logger.info "Attempted to add an entry to '#{filepath}', but an exception occurred: #{e}"
65
+ File.open(filepath, File::CREAT|File::RDWR) do |file|
66
+ unless file.any? { |line| line.chomp == value}
67
+ file.puts value
68
+ end
79
69
  end
80
- result
70
+ logger.info "Added an entry to '#{filepath}' successfully."
71
+ true
72
+ rescue IOError => e
73
+ logger.info "Attempted to add an entry to '#{filepath}', but an exception occurred: #{e}"
74
+ false
81
75
  end
82
76
 
83
77
  def remove_value_from_file(filepath, value)
84
- result = false
85
- begin
86
- raise "No such file: #{filepath}" unless File.exist?(filepath)
87
-
88
- found = false
89
- entries = open(filepath, File::RDONLY).readlines.collect do |l|
90
- entry = l.chomp
91
- if entry == value
92
- found = true
93
- nil
94
- elsif entry == ""
95
- nil
96
- else
97
- l
98
- end
99
- end.uniq.compact
100
- if found
101
- file = open(filepath, File::TRUNC | File::RDWR)
102
- file.write entries.join()
103
- file.close
104
- logger.info "Removed an entry from '#{filepath}' successfully."
105
- result = true
78
+
79
+ return true unless File.exist?(filepath)
80
+
81
+ found = false
82
+ entries = File.readlines(filepath).collect do |line|
83
+ entry = line.chomp
84
+ if entry == value
85
+ found = true
86
+ nil
87
+ elsif entry == ""
88
+ nil
106
89
  else
107
- raise Proxy::Salt::NotFound.new("Attempt to remove non-existent entry.")
90
+ line
108
91
  end
109
- rescue SystemCallError => e
110
- logger.info "Attempted to remove an entry from '#{filepath}', but an exception occurred: #{e}"
92
+ end.uniq.compact
93
+ if found
94
+ File.write(filepath, entries.join())
95
+ logger.info "Removed an entry from '#{filepath}' successfully."
111
96
  end
112
- result
97
+ true
98
+ rescue IOError => e
99
+ logger.info "Attempted to remove an entry from '#{filepath}', but an exception occurred: #{e}"
100
+ false
113
101
  end
114
102
 
115
103
  def highstate(host)
@@ -3,6 +3,6 @@
3
3
  module Proxy
4
4
  # Salt module
5
5
  module Salt
6
- VERSION = '4.0.0'.freeze
6
+ VERSION = '5.0.1'.freeze
7
7
  end
8
8
  end
@@ -17,13 +17,13 @@ If '/srv/salt' is configured as 'file_roots' in your '/etc/salt/master' config,
17
17
  /srv/salt/_runners/foreman_https.py
18
18
  ```
19
19
 
20
- Check if the reactor ('foreman_minion_auth.sls') is at the appropriated location and set the correct smart proxy address in the reactor file:
20
+ Check if the reactor ('foreman_minion_auth.sls') is at the appropriated location:
21
21
 
22
22
  ```
23
23
  /var/lib/foreman-proxy/salt/reactors/foreman_minion_auth.sls
24
24
  ```
25
25
 
26
- After changing the Salt master, restart the salt-master service:
26
+ Restart the salt-master service:
27
27
 
28
28
  ```
29
29
  systemctl restart salt-master
@@ -3,22 +3,8 @@
3
3
  {% if salt['saltutil.runner']('foreman_file.check_key', (data['id'], 100)) == True %}
4
4
  {%- do salt.log.info('Minion authenticated successfully, starting HTTPS request to delete autosign key.') -%}
5
5
  remove_autosign_key_custom_runner:
6
- runner.foreman_https.query_cert:
7
- - method: PUT
8
- - host: example.proxy.com # set smart proxy address
9
- - path: /salt/api/v2/salt_autosign_auth?name={{ data['id'] }}
10
- - cert: /etc/pki/katello/puppet/puppet_client.crt # default cert location
11
- - key: /etc/pki/katello/puppet/puppet_client.key # default key location
12
- - port: 443
13
- # Uncomment the following lines in case you want to use username + password authentication (not recommended)
14
- # call_foreman_salt_custom_runner:
15
- # runner.foreman_https.query_user:
16
- # - method: PUT
17
- # - host: example.proxy.com # set smart proxy address
18
- # - path: /salt/api/v2/salt_autosign_auth?name={{ data['id'] }}
19
- # - username: my_username # set username
20
- # - password: my_password # set password
21
- # - port: 443
6
+ runner.foreman_https.remove_key:
7
+ - minion: {{ data['id'] }}
22
8
  {% endif %}
23
9
  {% endif %}
24
10
  {% endif %}
@@ -1,10 +1,13 @@
1
- #!/usr/bin/env python
1
+ """
2
+ Salt runner to check the age of a minion key file.
3
+ """
2
4
 
3
5
  import os
4
6
  import time
5
7
 
6
8
  SALT_KEY_PATH = "/etc/salt/pki/master/minions/"
7
9
 
10
+
8
11
  def time_secs(path):
9
12
  stat = os.stat(path)
10
13
  return stat.st_mtime
@@ -18,5 +21,6 @@ def younger_than_secs(path, seconds):
18
21
  return True
19
22
  return False
20
23
 
24
+
21
25
  def check_key(hostname, seconds):
22
26
  return younger_than_secs(SALT_KEY_PATH + hostname, seconds)
@@ -1,13 +1,65 @@
1
- #!/usr/bin/env python
1
+ """
2
+ Salt runner to make generic https requests or perform directly
3
+ an autosign key removal.
4
+ """
5
+
2
6
 
3
7
  from http.client import HTTPSConnection
4
8
  import ssl
5
9
  import base64
6
10
  import json
11
+ import logging
12
+ import yaml
13
+
14
+ FOREMAN_CONFIG = '/etc/salt/foreman.yaml'
15
+ log = logging.getLogger(__name__)
16
+
17
+
18
+ def salt_config():
19
+ """
20
+ Read the foreman configuratoin from FOREMAN_CONFIG
21
+ """
22
+ with open(FOREMAN_CONFIG, 'r') as config_file:
23
+ config = yaml.load(config_file.read())
24
+ return config
25
+
26
+
27
+ def remove_key(minion):
28
+ """
29
+ Perform an HTTPS request to the configured foreman host and trigger
30
+ the autosign key removal process.
31
+ """
32
+ config = salt_config()
33
+ host_name = config[':host']
34
+ port = config[':port']
35
+ timeout = config[':timeout']
36
+ method = 'PUT'
37
+ path = '/salt/api/v2/salt_autosign_auth?name=%s' % minion
38
+
39
+ # Differentiate between cert and user authentication
40
+ if config[':proto'] == 'https':
41
+ query_cert(host=host_name,
42
+ path=path,
43
+ port=port,
44
+ method=method,
45
+ cert=config[':ssl_cert'],
46
+ key=config[':ssl_key'],
47
+ timeout=timeout)
48
+ else:
49
+ query_user(host=host_name,
50
+ path=path,
51
+ port=port,
52
+ method=method,
53
+ username=config[':username'],
54
+ password=config[':password'],
55
+ timeout=timeout)
7
56
 
8
57
 
9
58
  def query_cert(host, path, port, method, cert, key,
10
59
  payload=None, timeout=10):
60
+ """
61
+ Perform an HTTPS query with certificate credentials.
62
+ """
11
63
 
12
64
  headers = {"Accept": "application/json"}
13
65
 
@@ -38,6 +90,9 @@ def query_cert(host, path, port, method, cert, key,
38
90
 
39
91
  def query_user(host, path, port, method, username, password,
40
92
  payload=None, timeout=10):
93
+ """
94
+ Perform an HTTPS query with user credentials.
95
+ """
41
96
 
42
97
  auth = "{}:{}".format(username, password)
43
98
  token = base64.b64encode(auth.encode('utf-8')).decode('ascii')
@@ -1,19 +1,24 @@
1
1
  # Foreman Salt Report Upload
2
2
 
3
3
  Currently, there are two possibilites to upload the salt report to Foreman:
4
- 1. Use /usr/sbin/upload-salt-reports which is called by a cron job every 10 minutes by default
5
- 2. Upload the report immediately by using a Salt Reactor.
4
+ 1. Upload the report immediately by using a Salt Reactor (recommended).
5
+ 2. Use /usr/sbin/upload-salt-reports manually (or set up a cronjob, see `/cron/smart_proxy_salt`).
6
6
 
7
- This README, handles the second option and how to configure it
7
+ This README handles the first option and how to configure it
8
8
 
9
9
  ## Setup
10
10
  Add the content of 'master.snippet' to '/etc/salt/master' which configures a reactor.
11
11
  In case there is already a reactor configured, you need to adapt it using the options mentioned in 'master.snippet'.
12
12
 
13
- In case '/srv/salt' is configured as 'file_roots' in your '/etc/salt/master' config, setup the necessary salt state file and Salt runner functions:
13
+ Check the reactor file to be in the following folder (or a different one depending on your master configuration):
14
+
15
+ ```
16
+ /var/lib/foreman-proxy/salt/reactors/foreman_report_upload.sls
17
+ ```
18
+
19
+ In case '/srv/salt' is configured as 'file_roots' in your '/etc/salt/master' config, setup the necessary Salt runner:
14
20
 
15
21
  ```
16
- /srv/salt/foreman_report_upload.sls
17
22
  /srv/salt/_runners/foreman_report_upload.py
18
23
  ```
19
24
 
@@ -1,3 +1,3 @@
1
1
  reactor:
2
2
  - 'salt/job/*/ret/*':
3
- - /srv/salt/foreman_report_upload.sls
3
+ - /var/lib/foreman-proxy/salt/reactors/foreman_report_upload.sls
@@ -4,13 +4,16 @@ Uploads reports from the Salt job cache to Foreman
4
4
  '''
5
5
  from __future__ import absolute_import, print_function, unicode_literals
6
6
 
7
+ LAST_UPLOADED = '/etc/salt/last_uploaded'
7
8
  FOREMAN_CONFIG = '/etc/salt/foreman.yaml'
9
+ LOCK_FILE = '/var/lock/salt-report-upload.lock'
8
10
 
9
11
  try:
10
12
  from http.client import HTTPConnection, HTTPSConnection
11
13
  except ImportError:
12
14
  from httplib import HTTPSConnection, HTTPSConnection
13
15
 
16
+ import io
14
17
  import ssl
15
18
  import json
16
19
  import yaml
@@ -24,12 +27,21 @@ import logging
24
27
  log = logging.getLogger(__name__)
25
28
 
26
29
 
30
+ if sys.version_info.major == 3:
31
+ unicode = str
32
+
33
+
27
34
  def salt_config():
28
35
  with open(FOREMAN_CONFIG, 'r') as f:
29
36
  config = yaml.load(f.read())
30
37
  return config
31
38
 
32
39
 
40
+ def write_last_uploaded(last_uploaded):
41
+ with io.open(LAST_UPLOADED, 'w+') as f:
42
+ f.write(unicode(last_uploaded))
43
+
44
+
33
45
  def upload(report):
34
46
  config = salt_config()
35
47
  headers = {'Accept': 'application/json',
@@ -55,6 +67,7 @@ def upload(report):
55
67
  response = connection.getresponse()
56
68
 
57
69
  if response.status == 200:
70
+ write_last_uploaded(report['job']['job_id'])
58
71
  info_msg = 'Success {0}: {1}'.format(report['job']['job_id'], response.read())
59
72
  log.info(info_msg)
60
73
  else:
@@ -81,7 +94,7 @@ def create_report(json_str):
81
94
  return {'job':
82
95
  {
83
96
  'result': {
84
- msg['id']: entry['changes']['ret'],
97
+ msg['id']: next(iter(entry['changes'].values())),
85
98
  },
86
99
  'function': 'state.highstate',
87
100
  'job_id': msg['jid']
@@ -90,6 +103,18 @@ def create_report(json_str):
90
103
  raise Exception('No state.highstate found')
91
104
 
92
105
 
106
+ def get_lock():
107
+ if os.path.isfile(LOCK_FILE):
108
+ raise Exception("Unable to obtain lock.")
109
+ else:
110
+ io.open(LOCK_FILE, 'w+').close()
111
+
112
+
113
+ def release_lock():
114
+ if os.path.isfile(LOCK_FILE):
115
+ os.remove(LOCK_FILE)
116
+
117
+
93
118
  def now(highstate):
94
119
  '''
95
120
  Upload a highstate to Foreman
@@ -100,7 +125,10 @@ def now(highstate):
100
125
 
101
126
  try:
102
127
  report = create_report(base64.b64decode(highstate))
128
+ get_lock()
103
129
  upload(report)
104
130
  except Exception as exc:
105
131
  log.error('Exception encountered: %s', exc)
132
+ finally:
133
+ release_lock()
106
134
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_salt
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Moll
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-08-02 00:00:00.000000000 Z
12
+ date: 2022-11-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: test-unit
@@ -124,7 +124,7 @@ files:
124
124
  - bin/foreman-node
125
125
  - bin/salt_python_wrapper
126
126
  - bundler.d/salt.rb
127
- - cron/smart_proxy_salt
127
+ - etc/foreman.conf.example
128
128
  - etc/foreman.yaml.example
129
129
  - lib/smart_proxy_salt.rb
130
130
  - lib/smart_proxy_salt/api_request.rb
@@ -142,9 +142,9 @@ files:
142
142
  - salt/minion_auth/srv/salt/_runners/foreman_file.py
143
143
  - salt/minion_auth/srv/salt/_runners/foreman_https.py
144
144
  - salt/report_upload/README.md
145
+ - salt/report_upload/foreman_report_upload.sls
145
146
  - salt/report_upload/master.snippet
146
147
  - salt/report_upload/srv/salt/_runners/foreman_report_upload.py
147
- - salt/report_upload/srv/salt/foreman_report_upload.sls
148
148
  - sbin/upload-salt-reports
149
149
  - settings.d/salt.saltfile.example
150
150
  - settings.d/salt.yml.example
@@ -1,5 +0,0 @@
1
- SHELL=/bin/sh
2
- PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
3
-
4
- # Check every 10 minutes for new Salt reports to upload to Foreman
5
- */10 * * * * root /usr/sbin/upload-salt-reports >>/var/log/foreman-proxy/salt-cron.log 2>&1