Skip to content

Commit f7af12c

Browse files
balasankarcRobert Marshall
andcommitted
Merge branch '7452-add-userspace-way-to-reapply-sepolicy' into 'master'
Enable easy SELinux context fixes Closes #7452 See merge request https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/7486 Merged-by: Balasankar 'Balu' C <[email protected]> Approved-by: Ryan Egesdahl <[email protected]> Approved-by: Balasankar 'Balu' C <[email protected]> Co-authored-by: Robert Marshall <[email protected]>
2 parents f09ef50 + 9ef2e18 commit f7af12c

File tree

6 files changed

+168
-53
lines changed

6 files changed

+168
-53
lines changed

doc/troubleshooting.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,14 @@ command is available.
174174
#### Diagnose and resolve SELinux issues
175175

176176
Omnibus GitLab detects default path changes in `/etc/gitlab/gitlab.rb` and should apply
177-
the correct file contexts. For installations using custom data path configuration,
177+
the correct file contexts.
178+
179+
NOTE:
180+
From GitLab 16.10 forward, administrators can try `gitlab-ctl apply-sepolicy`
181+
to automatically fix SELinux issues. Consult
182+
`gitlab-ctl apply-sepolicy --help` for runtime options.
183+
184+
For installations using custom data path configuration,
178185
the administrator may have to manually resolve SELinux issues.
179186

180187
Data paths may be altered via `gitlab.rb`, however, a common scenario forces the

files/gitlab-cookbooks/package/libraries/helpers/selinux_helper.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def use_unified_policy?(node)
1010
true
1111
end
1212

13-
def commands(node)
13+
def commands(node, dry_run: false)
1414
ssh_dir = File.join(node['gitlab']['user']['home'], ".ssh")
1515
authorized_keys = node['gitlab']['gitlab_shell']['auth_file']
1616
gitlab_shell_var_dir = node['gitlab']['gitlab_shell']['dir']
@@ -19,12 +19,14 @@ def commands(node)
1919
gitlab_rails_etc_dir = File.join(gitlab_rails_dir, "etc")
2020
gitlab_shell_secret_file = File.join(gitlab_rails_etc_dir, 'gitlab_shell_secret')
2121
gitlab_workhorse_sockets_directory = node['gitlab']['gitlab_workhorse']['sockets_directory']
22+
restorecon_flags = "-v"
23+
restorecon_flags << " -n" if dry_run
2224

2325
# If SELinux is enabled, make sure that OpenSSH thinks the .ssh directory and authorized_keys file of the
2426
# git_user is valid.
2527
selinux_code = []
2628
selinux_code << "semanage fcontext -a -t gitlab_shell_t '#{ssh_dir}(/.*)?'"
27-
selinux_code << "restorecon -R -v '#{ssh_dir}'" if File.exist?(ssh_dir)
29+
selinux_code << "restorecon -R #{restorecon_flags} '#{ssh_dir}'" if File.exist?(ssh_dir)
2830
[
2931
authorized_keys,
3032
gitlab_shell_config_file,
@@ -34,7 +36,7 @@ def commands(node)
3436
selinux_code << "semanage fcontext -a -t gitlab_shell_t '#{file}'"
3537
next unless File.exist?(file)
3638

37-
selinux_code << "restorecon -v '#{file}'"
39+
selinux_code << "restorecon #{restorecon_flags} '#{file}'"
3840
end
3941

4042
selinux_code.join("\n")
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
#
13+
14+
# For testing purposes, if the first path cannot be found load the second
15+
begin
16+
require_relative '../../../../cookbooks/package/libraries/helpers/selinux_helper.rb'
17+
rescue LoadError
18+
require_relative '../../../gitlab-cookbooks/package/libraries/helpers/selinux_helper.rb'
19+
end
20+
21+
module GitlabCtl
22+
class SELinuxManager
23+
class << self
24+
def parse_options(args, banner)
25+
options = { verbose: false, dry_run: false }
26+
27+
begin
28+
OptionParser.new do |opts|
29+
opts.banner = banner
30+
opts.on('-v', '--verbose', 'Show all output.') do |v|
31+
options[:verbose] = v
32+
end
33+
opts.on('-d', '--dry-run', 'Show what would change.') do |d|
34+
options[:dry_run] = d
35+
options[:verbose] = true
36+
end
37+
end.parse!(args)
38+
rescue OptionParser::InvalidOption
39+
args << '-h'
40+
options = parse_options(args, banner)
41+
end
42+
43+
options
44+
end
45+
end
46+
end
47+
end

files/gitlab-ctl-commands/selinux.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
#
13+
14+
require "#{base_path}/embedded/service/omnibus-ctl/lib/gitlab_ctl/selinux"
15+
require "#{base_path}/embedded/service/omnibus-ctl/lib/gitlab_ctl/util"
16+
17+
add_command_under_category('apply-sepolicy',
18+
'SELinux Controls',
19+
'Apply GitLab SELinux policy to managed files',
20+
2) do
21+
options = GitlabCtl::SELinuxManager.parse_options(ARGV, "Usage: gitlab-ctl apply-sepolicy [options]")
22+
23+
node_attributes = GitlabCtl::Util.get_node_attributes
24+
25+
result = GitlabCtl::Util.run_command(SELinuxHelper.commands(node_attributes, dry_run: options[:dry_run]))
26+
27+
log result.stdout if options[:verbose] && !result.stdout.empty?
28+
rescue GitlabCtl::Errors::NodeError => e
29+
log "Cannot apply SELinux policy and contexts. #{e}"
30+
end

spec/chef/cookbooks/gitlab/recipes/selinux_spec.rb

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,63 +22,14 @@
2222
context 'when running on selinux' do
2323
before do
2424
allow_any_instance_of(ShellOutHelper).to receive(:success?).with('id -Z').and_return(true)
25-
allow(File).to receive(:exist?).and_call_original
26-
allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh').and_return(true)
27-
allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh/authorized_keys').and_return(true)
28-
allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret').and_return(true)
29-
allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-shell/config.yml').and_return(true)
30-
allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-workhorse/sockets').and_return(true)
3125
end
3226

3327
let(:bash_block) { chef_run.bash('Set proper security context on ssh files for selinux') }
3428

35-
def semanage_fcontext(filename)
36-
"semanage fcontext -a -t gitlab_shell_t '#{filename}'"
37-
end
38-
3929
it 'should run the semanage bash command' do
4030
expect(templatesymlink).to notify('bash[Set proper security context on ssh files for selinux]').delayed
4131
end
4232

43-
it 'sets the security context of gitlab-shell files' do
44-
lines = bash_block.code.split("\n")
45-
files = %w(/var/opt/gitlab/.ssh(/.*)?
46-
/var/opt/gitlab/.ssh/authorized_keys
47-
/var/opt/gitlab/gitlab-shell/config.yml
48-
/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret
49-
/var/opt/gitlab/gitlab-workhorse/sockets)
50-
managed_files = files.map { |file| semanage_fcontext(file) }
51-
52-
expect(lines).to include(*managed_files)
53-
expect(lines).to include("restorecon -R -v '/var/opt/gitlab/.ssh'")
54-
expect(lines).to include("restorecon -v '/var/opt/gitlab/.ssh/authorized_keys'")
55-
expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-shell/config.yml'")
56-
expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret'")
57-
expect(lines).to include("restorecon -v '/var/opt/gitlab/gitlab-workhorse/sockets'")
58-
end
59-
60-
context 'and the user configured a custom workhorse sockets directory' do
61-
let(:user_sockets_directory) { '/how/do/you/do' }
62-
before do
63-
stub_gitlab_rb(
64-
gitlab_workhorse: {
65-
listen_network: 'unix',
66-
sockets_directory: user_sockets_directory
67-
}
68-
)
69-
end
70-
71-
it 'sets the security context of a custom workhorse sockets directory' do
72-
allow(File).to receive(:exist?).with(user_sockets_directory).and_return(true)
73-
lines = bash_block.code.split("\n")
74-
files = [user_sockets_directory]
75-
managed_files = files.map { |file| semanage_fcontext(file) }
76-
77-
expect(lines).to include(*managed_files)
78-
expect(lines).to include("restorecon -v '#{user_sockets_directory}'")
79-
end
80-
end
81-
8233
context 'when gitlab-rails is disabled' do
8334
before do
8435
stub_gitlab_rb(
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# frozen_string_literal: true
2+
require 'chef_helper'
3+
4+
RSpec.describe SELinuxHelper do
5+
let(:chef_run) { converge_config }
6+
7+
context 'when building SELinux policy command strings' do
8+
before do
9+
allow(File).to receive(:exist?).and_call_original
10+
allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh').and_return(true)
11+
allow(File).to receive(:exist?).with('/var/opt/gitlab/.ssh/authorized_keys').and_return(true)
12+
allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret').and_return(true)
13+
allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-shell/config.yml').and_return(true)
14+
allow(File).to receive(:exist?).with('/var/opt/gitlab/gitlab-workhorse/sockets').and_return(true)
15+
end
16+
17+
def semanage_fcontext(filename)
18+
"semanage fcontext -a -t gitlab_shell_t '#{filename}'"
19+
end
20+
21+
using RSpec::Parameterized::TableSyntax
22+
where(:dry_run, :restorecon_options) do
23+
true | '-v -n'
24+
false | '-v'
25+
end
26+
27+
with_them do
28+
let(:node) { chef_run.node }
29+
let(:lines) { SELinuxHelper.commands(node, dry_run: dry_run) }
30+
31+
it 'adds the correct parameters to restorecon' do
32+
expect(lines).to include("restorecon -R #{restorecon_options} '/var/opt/gitlab/.ssh'")
33+
expect(lines).to include("restorecon #{restorecon_options} '/var/opt/gitlab/.ssh/authorized_keys'")
34+
expect(lines).to include("restorecon #{restorecon_options} '/var/opt/gitlab/gitlab-shell/config.yml'")
35+
expect(lines).to include("restorecon #{restorecon_options} '/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret'")
36+
expect(lines).to include("restorecon #{restorecon_options} '/var/opt/gitlab/gitlab-workhorse/sockets'")
37+
end
38+
39+
it 'adds the correct SELinux file contexts' do
40+
files = %w(/var/opt/gitlab/.ssh(/.*)?
41+
/var/opt/gitlab/.ssh/authorized_keys
42+
/var/opt/gitlab/gitlab-shell/config.yml
43+
/var/opt/gitlab/gitlab-rails/etc/gitlab_shell_secret
44+
/var/opt/gitlab/gitlab-workhorse/sockets)
45+
46+
managed_files = files.map { |file| semanage_fcontext(file) }
47+
expect(lines).to include(*managed_files)
48+
end
49+
end
50+
51+
with_them do
52+
let(:user_sockets_directory) { '/how/do/you/do' }
53+
let(:node) { chef_run.node }
54+
let(:lines) { SELinuxHelper.commands(node, dry_run: dry_run) }
55+
56+
before do
57+
allow(Gitlab).to receive(:[]).and_call_original
58+
stub_gitlab_rb(
59+
gitlab_workhorse: {
60+
listen_network: 'unix',
61+
sockets_directory: user_sockets_directory
62+
}
63+
)
64+
allow(File).to receive(:exist?).with(user_sockets_directory).and_return(true)
65+
end
66+
67+
context 'when the user sets a custom workhorse sockets directory' do
68+
it 'applies the security context to the custom workhorse sockets directory' do
69+
files = [user_sockets_directory]
70+
managed_files = files.map { |file| semanage_fcontext(file) }
71+
72+
expect(lines).to include(*managed_files)
73+
expect(lines).to include("restorecon #{restorecon_options} '#{user_sockets_directory}'")
74+
end
75+
end
76+
end
77+
end
78+
end

0 commit comments

Comments
 (0)