Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/services/foreman/renderer/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Configuration
:template_name,
:dns_lookup,
:pxe_kernel_options,
:append_to_file,
:save_to_file,
:subnet_param, :subnet_has_param?,
:global_setting,
Expand Down Expand Up @@ -57,6 +58,7 @@ class Configuration
:foreman_server_ca_cert,
:format_time,
:shell_escape,
:expand_path,
:join_with_line_break,
:current_date,
:current_time,
Expand Down
27 changes: 27 additions & 0 deletions app/services/foreman/renderer/scope/macros/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,33 @@ def save_to_file(filename, content, verbatim: false)
end
end

apipie :method, 'Generates a shell command to append the given text into a file' do
desc "This is useful if some multiline string needs to be appended to some file on the hard disk. This
is typically used in provisioning or job templates, e.g. when ssh keys need to be appended to the
authorized_keys file. The content must end with a line end, if not an extra trailing line end is
appended automatically unless the content is empty. Note that, the file name or path is printed as
it is without any escaping even if it contains any whitespace or special charecters. In order to
escape the special charecters, process the file name using the shell_escape function."
required :filename, String, desc: 'The file path to append the content to'
required :content, String, desc: 'Content to be appended'
keyword :verbatim, [true, false], desc: 'Controls whether the file should be put on disk as-is or if variables should be replaced by shell before the file is written out', default: false
returns String, desc: 'String representing the shell command'
example "append_to_file('/etc/motd', \"hello\\nworld\\n\") # => 'cat << EOF-0e4f089a >> /etc/motd\\nhello\\nworld\\nEOF-0e4f089a'"
example "append_to_file(shell_escape('/tmp/a file with spaces'), nil) # => 'touch /tmp/a\ file\ with\ spaces'"
end
def append_to_file(filename, content, verbatim: false)
delimiter = 'EOF-' + Digest::SHA512.hexdigest(filename)[0..7]
if content.empty?
"touch #{filename}"
elsif verbatim
content = Base64.encode64(content)
"cat << #{delimiter} | base64 -d >> #{filename}\n#{content}#{delimiter}"
else
content += "\n" unless content.end_with?("\n")
"cat << #{delimiter} >> #{filename}\n#{content}#{delimiter}"
end
end

apipie :method, desc: 'Takes a block of code, runs it and prefixes the resulting text by given number of spaces' do
desc "This is useful when rendering output is a whitespace sensitive format, such as YAML."
required :count, Integer, desc: 'The number of spaces'
Expand Down
9 changes: 9 additions & 0 deletions app/services/foreman/renderer/scope/macros/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ def shell_escape(string)
Shellwords.shellescape(string)
end

apipie :method, "Expand file path to be able to safely process it using shell_escape" do
required :string, String, desc: 'File path to expand'
returns String
example "expand_path('~root/.ssh/authorized_keys') #=> '/root/.ssh/authorized_keys'"
end
def expand_path(path)
File.expand_path(path)
end

apipie :method, 'Returns current date' do
keyword :format, String, desc: 'Format string to format date according to the directives in this string', default: '%F'
returns String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ description: |

remote_execution_ssh_keys: public keys to be put in ~/.ssh/authorized_keys

remote_execution_ssh_ca_keys: public ssh CA keys to be put in
/etc/ssh/foreman-user-ca.pub

remote_execution_ssh_user: user for which remote_execution_ssh_keys will be
authorized

Expand All @@ -33,7 +36,7 @@ if [ -z "$PKG_MANAGER" ]; then
<%= indent(2) { snippet 'pkg_manager' } -%>
fi

<% if !host_param('remote_execution_ssh_keys').blank? %>
<% if host_param('remote_execution_ssh_keys').present? || host_param('remote_execution_ssh_ca_keys').present? %>
<% ssh_user = host_param('remote_execution_ssh_user') || 'root' %>

user_exists=false
Expand All @@ -46,13 +49,12 @@ fi
<% end -%>

if $user_exists; then
<% if host_param('remote_execution_ssh_keys').present? -%>
<% ssh_path = "~#{ssh_user}/.ssh" %>

mkdir -p <%= ssh_path %>

cat << EOF >> <%= ssh_path %>/authorized_keys
<%= host_param('remote_execution_ssh_keys').is_a?(String) ? host_param('remote_execution_ssh_keys') : host_param('remote_execution_ssh_keys').join("\n") %>
EOF
<%= append_to_file(shell_escape(expand_path("#{ssh_path}/authorized_keys")), host_param('remote_execution_ssh_keys').is_a?(String) ? host_param('remote_execution_ssh_keys') : host_param('remote_execution_ssh_keys').join("\n")) %>

chmod 0700 <%= ssh_path %>
chmod 0600 <%= ssh_path %>/authorized_keys
Expand All @@ -63,6 +65,41 @@ EOF

# Restore SELinux context with restorecon, if it's available:
command -v restorecon && restorecon -RvF <%= ssh_path %> || true
<% end -%>

<% user_ca_keys = host_param('remote_execution_ssh_ca_keys') %>
<% if user_ca_keys.present? -%>
mkdir -p /etc/ssh/sshd_config.d

<% user_ca_path = '/etc/ssh/foreman-user-ca.pub' %>
<%= save_to_file(user_ca_path, user_ca_keys.is_a?(String) ? user_ca_keys : user_ca_keys.join("\n")) %>

chmod 0644 <%= user_ca_path %>

<%= save_to_file('/etc/ssh/sshd_config.d/60-foreman-user-ca.conf', "TrustedUserCAKeys #{user_ca_path}") %>

command -v restorecon && restorecon -RvF /etc/ssh || true

systemctl try-restart <%= @host.operatingsystem&.family == 'Debian' ? 'ssh' : 'sshd' %>
<% end -%>

<% user_ca_keys = host_param('remote_execution_ssh_ca_keys') %>
<% if user_ca_keys.present? -%>
mkdir -p /etc/ssh/sshd_config.d

<% user_ca_path = '/etc/ssh/foreman-user-ca.pub' %>
<%= save_to_file(user_ca_path, user_ca_keys.is_a?(String) ? user_ca_keys : user_ca_keys.join("\n")) %>

chmod 0644 <%= user_ca_path %>

<%= save_to_file('/etc/ssh/sshd_config.d/60-foreman-user-ca.conf', "TrustedUserCAKeys #{user_ca_path}") %>

command -v restorecon && restorecon -RvF /etc/ssh || true

# restart the sshd service
$(command -v cloud-init && cloud-init status --wait) >/dev/null 2>&1 || true
systemctl restart <%= @host.operatingsystem.family == 'Debian' ? 'ssh' : 'sshd' %>
<% end -%>

<% if ssh_user != 'root' && host_param('remote_execution_effective_user_method') == 'sudo' -%>
if [ ! -x "$(command -v sudo)" ]; then
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%#
kind: snippet
name: restart_sshd
model: ProvisioningTemplate
snippet: true
description: |
This snippet restarts the sshd service

Parameters:

remote_execution_ssh_ca_keys: public ssh CA keys
-%>

systemctl try-restart <%= @host.operatingsystem&.family == 'Debian' ? 'ssh' : 'sshd' %>
echo "Restarted sshd to apply CA keys" > /tmp/ssh_ca_keys_applied
Loading