smart_proxy_salt 4.0.0 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
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