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 +4 -4
- data/etc/foreman.conf.example +65 -0
- data/lib/smart_proxy_salt/cli.rb +29 -41
- data/lib/smart_proxy_salt/version.rb +1 -1
- data/salt/minion_auth/README.md +2 -2
- data/salt/minion_auth/foreman_minion_auth.sls +2 -16
- data/salt/minion_auth/srv/salt/_runners/foreman_file.py +5 -1
- data/salt/minion_auth/srv/salt/_runners/foreman_https.py +56 -1
- data/salt/report_upload/README.md +10 -5
- data/salt/report_upload/{srv/salt/foreman_report_upload.sls → foreman_report_upload.sls} +0 -0
- data/salt/report_upload/master.snippet +1 -1
- data/salt/report_upload/srv/salt/_runners/foreman_report_upload.py +29 -1
- metadata +4 -4
- data/cron/smart_proxy_salt +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ba18b31812c408bbbef40601e2f7279ee6f04d55a62b999b7b993f0158aede8
|
4
|
+
data.tar.gz: 82822b47407c7c5e66151627a7935d294566c6e5dc17c4bbf7c2674890653826
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/smart_proxy_salt/cli.rb
CHANGED
@@ -62,54 +62,42 @@ module Proxy
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def append_value_to_file(filepath, value)
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
90
|
+
line
|
108
91
|
end
|
109
|
-
|
110
|
-
|
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
|
-
|
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)
|
data/salt/minion_auth/README.md
CHANGED
@@ -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
|
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
|
-
|
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.
|
7
|
-
-
|
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
|
-
|
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
|
-
|
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.
|
5
|
-
2.
|
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
|
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
|
-
|
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
|
|
File without changes
|
@@ -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']
|
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
|
+
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:
|
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
|
-
-
|
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
|
data/cron/smart_proxy_salt
DELETED