Skip to content

Commit 7a28b42

Browse files
author
Brent Boe
authored
Merge pull request #222 from splunk/no-sudo-when-running-as-splunk
Allow unprivileged mode when running as splunk user
2 parents 805f945 + 0450046 commit 7a28b42

File tree

5 files changed

+151
-44
lines changed

5 files changed

+151
-44
lines changed

docs/SECURITY.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
## Security ##
2+
This section will cover various security considerations when using the Splunk Enterprise and Universal Forwarder containers.
3+
4+
### Startup Users ###
5+
6+
The Splunk Enterprise and Universal Forwarder containers may be started using one of the following three user accounts:
7+
8+
* `splunk` (most secure): This user has no privileged access and cannot use `sudo` to change to another user account.
9+
It is a member of the `ansible` group, which enables it to run the embedded playbooks at startup. When using the
10+
`splunk` user, all processes will run as this user. Note that you must set the `SPLUNK_HOME_OWNERSHIP_ENFORCEMENT`
11+
environment variable to `false` when starting as this user. ***Recommended for production***
12+
13+
* `ansible` (middle ground): This user is a member of the `sudo` group and able to execute `sudo` commands without a
14+
password. It uses privileged access at startup only to perform certain actions which cannot be performed by regular
15+
users (see below). After startup, `sudo` access will automatically be removed from the `ansible` user if the
16+
environment variable `STEPDOWN_ANSIBLE_USER` is set to `true`. ***This is the default user account***
17+
18+
* `root` (least secure): This is a privileged user running with UID of `0`. Some customers may want to use this for
19+
forwarder processes that require access to log files which cannot be read by any other user. ***This is not recommended***
20+
21+
### After Startup ###
22+
23+
By default, the primary Splunk processes will always run as the unprivileged user and group `splunk`,
24+
irregardless of which user account the containers are started with. You can override this by changing the following:
25+
26+
* User: `splunk.user` variable in your `default.yml` template, or the `SPLUNK_USER` environment variable
27+
* Group: `splunk.group` variable in your `default.yml` template, or the `SPLUNK_GROUP` environment variable
28+
29+
Note that the containers are built with the `splunk` user having UID `41812` and the `splunk` group having GID `41812`.
30+
31+
You may want to override these settings to ensure that Splunk forwarder processes have access to read your log files.
32+
For example, you can ensure that all processes run as `root` by starting as the `root` user with the environment
33+
variable `SPLUNK_USER` also set to `root` (this is not recommended).
34+
35+
### Privileged Features ###
36+
37+
Certain features supported by the Splunk Enterprise and Universal Forwarder containers require that they are started
38+
with privileged access using either the `ansible` or `root` user accounts.
39+
40+
#### Splunk Home Ownership ####
41+
42+
By default, at startup the containers will ensure that all files located under the `SPLUNK_HOME` directory
43+
(`/opt/splunk`) are owned by user `splunk` and group `splunk`. This helps to ensure that the Splunk processes are
44+
able to read and write any external volumes mounted for `/opt/splunk/etc` and `/opt/splunk/var`. While all supported
45+
versions of the docker engine will automatically set proper ownership for these volumes, external orchestration systems
46+
typically will require extra steps.
47+
48+
If you know that this step is unnecessary, you can disable it by setting the `SPLUNK_HOME_OWNERSHIP_ENFORCEMENT`
49+
environment variable to `false`. Note that this must be disabled when starting containers with the `splunk` user
50+
account.
51+
52+
#### Package Installation ####
53+
54+
The `JAVA_VERSION` environment variable can be used to automatically install OpenJDK at startup time. This feature
55+
requires starting as a privileged user account.
56+
57+
### Kubernetes Users ###
58+
59+
For Kubernetes, we recommend using the `fsGroup` [Security Context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
60+
to ensure that all Pods are able to write to your Persistent Volumes. For example:
61+
62+
```
63+
apiVersion: v1
64+
kind: Pod
65+
metadata:
66+
name: example-splunk-pod
67+
spec:
68+
securityContext:
69+
runAsUser: 41812
70+
fsGroup: 41812
71+
containers:
72+
name: example-splunk-container
73+
image: splunk/splunk
74+
env:
75+
- name: SPLUNK_HOME_OWNERSHIP_ENFORCEMENT
76+
value: "false"
77+
...
78+
```
79+
80+
This can be used to create a Splunk Enterprise Pod running as the unprivileged `splunk` user which is able to securely
81+
read and write from any Persistent Volumes that are created for it.
82+
83+
Red Hat OpenShift users can leverage the built-in `nonroot` [Security Context Constraint](https://docs.openshift.com/container-platform/3.9/admin_guide/manage_scc.html)
84+
to run Pods with the above Security Context:
85+
```
86+
oc adm policy add-scc-to-user nonroot default
87+
```

splunk/common-files/Dockerfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,15 @@ RUN sed -i -e 's/%sudo\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%sudo ALL=NOPASSWD:ALL/g' /
101101
&& groupadd -r ${ANSIBLE_GROUP} \
102102
&& useradd -r -m -g ${ANSIBLE_GROUP} ${ANSIBLE_USER} \
103103
&& usermod -aG sudo ${ANSIBLE_USER} \
104+
&& usermod -aG ${ANSIBLE_GROUP} ${SPLUNK_USER} \
104105
# Container Artifact Directory is a place for all artifacts and logs that are generated by the provisioning process. The directory is owned by the user "ansible".
105106
&& mkdir ${CONTAINER_ARTIFACT_DIR} \
106-
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} $CONTAINER_ARTIFACT_DIR \
107+
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} ${CONTAINER_ARTIFACT_DIR} \
108+
&& chmod -R 775 ${CONTAINER_ARTIFACT_DIR} \
107109
&& chmod -R 555 ${SPLUNK_ANSIBLE_HOME} \
108-
&& chmod -R 777 ${CONTAINER_ARTIFACT_DIR} \
110+
&& chgrp ${ANSIBLE_GROUP} ${SPLUNK_ANSIBLE_HOME} ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
111+
&& chmod 775 ${SPLUNK_ANSIBLE_HOME} \
112+
&& chmod 664 ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
109113
&& chmod 755 /sbin/entrypoint.sh /sbin/createdefaults.py /sbin/checkstate.sh
110114

111115
USER ${ANSIBLE_USER}

splunk/common-files/entrypoint.sh

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ trap teardown SIGINT SIGTERM
3535

3636
prep_ansible() {
3737
cd ${SPLUNK_ANSIBLE_HOME}
38+
if [ `whoami` == "${SPLUNK_USER}" ]; then
39+
sed -i -e "s,^become\\s*=.*,become = false," ansible.cfg
40+
fi
3841
if [[ "$DEBUG" == "true" ]]; then
3942
ansible-playbook --version
4043
python inventory/environ.py --write-to-file
@@ -53,34 +56,37 @@ watch_for_failure(){
5356
echo Ansible playbook complete, will begin streaming var/log/splunk/splunkd_stderr.log
5457
echo
5558
user_permission_change
59+
if [ `whoami` != "${SPLUNK_USER}" ]; then
60+
RUN_AS_SPLUNK="sudo -u ${SPLUNK_USER}"
61+
fi
5662
# Any crashes/errors while Splunk is running should get logged to splunkd_stderr.log and sent to the container's stdout
5763
if [ -z "$SPLUNK_TAIL_FILE" ]; then
58-
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
64+
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
5965
else
60-
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
66+
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
6167
fi
6268
wait
6369
}
6470

6571
create_defaults() {
66-
createdefaults.py
72+
createdefaults.py
6773
}
6874

6975
start_and_exit() {
70-
if [ -z "$SPLUNK_PASSWORD" ]
71-
then
72-
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
73-
fi
76+
if [ -z "$SPLUNK_PASSWORD" ]
77+
then
78+
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
79+
fi
7480
sh -c "echo 'starting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
7581
setup
76-
prep_ansible
82+
prep_ansible
7783
ansible-playbook $ANSIBLE_EXTRA_FLAGS -i inventory/environ.py site.yml
7884
}
7985

8086
start() {
81-
trap teardown EXIT
87+
trap teardown EXIT
8288
start_and_exit
83-
watch_for_failure
89+
watch_for_failure
8490
}
8591

8692
configure_multisite() {
@@ -89,17 +95,17 @@ configure_multisite() {
8995
}
9096

9197
restart(){
92-
trap teardown EXIT
98+
trap teardown EXIT
9399
sh -c "echo 'restarting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
94-
prep_ansible
95-
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
100+
prep_ansible
101+
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
96102
ansible-playbook -i inventory/environ.py start.yml
97103
watch_for_failure
98104
}
99105

100106
user_permission_change(){
101107
if [[ "$STEPDOWN_ANSIBLE_USER" == "true" ]]; then
102-
bash -c "sudo deluser -q ansible sudo"
108+
bash -c "sudo deluser -q ansible sudo"
103109
fi
104110
}
105111

@@ -140,7 +146,7 @@ Examples:
140146
* docker run -it -e SPLUNK_START_ARGS=--accept-license -e SPLUNK_INDEXER_URL=idx1,idx2 -e SPLUNK_SEARCH_HEAD_URL=sh1,sh2 -e SPLUNK_ROLE=splunk_search_head --hostname sh1 --network splunknet --network-alias sh1 -e SPLUNK_PASSWORD=helloworld -e SPLUNK_LICENSE_URI=http://example.com/splunk.lic splunk/splunk start
141147
142148
EOF
143-
exit 1
149+
exit 1
144150
}
145151

146152
case "$1" in
@@ -157,12 +163,12 @@ case "$1" in
157163
configure_multisite $0
158164
;;
159165
create-defaults)
160-
create_defaults
161-
;;
166+
create_defaults
167+
;;
162168
restart)
163-
shift
164-
restart $@
165-
;;
169+
shift
170+
restart $@
171+
;;
166172
no-provision)
167173
user_permission_change
168174
tail -n 0 -f /etc/hosts &

uf/common-files/Dockerfile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,15 @@ RUN \
9090
&& groupadd -r ${ANSIBLE_GROUP} \
9191
&& useradd -r -m -g ${ANSIBLE_GROUP} ${ANSIBLE_USER} \
9292
&& usermod -aG sudo ${ANSIBLE_USER} \
93+
&& usermod -aG ${ANSIBLE_GROUP} ${SPLUNK_USER} \
9394
# Container Artifact Directory is a place for all artifacts and logs that are generated by the provisioning process. The directory is owned by the user "ansible".
9495
&& mkdir ${CONTAINER_ARTIFACT_DIR} \
95-
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} $CONTAINER_ARTIFACT_DIR \
96+
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} ${CONTAINER_ARTIFACT_DIR} \
97+
&& chmod -R 775 ${CONTAINER_ARTIFACT_DIR} \
9698
&& chmod -R 555 ${SPLUNK_ANSIBLE_HOME} \
99+
&& chgrp ${ANSIBLE_GROUP} ${SPLUNK_ANSIBLE_HOME} ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
100+
&& chmod 775 ${SPLUNK_ANSIBLE_HOME} \
101+
&& chmod 664 ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
97102
&& chmod 755 /sbin/entrypoint.sh /sbin/createdefaults.py /sbin/checkstate.sh
98103

99104
USER ${ANSIBLE_USER}

uf/common-files/entrypoint.sh

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ setup() {
2020
# Check if the user accepted the license
2121
if [[ "$SPLUNK_START_ARGS" != *"--accept-license"* ]]; then
2222
printf "License not accepted, please ensure the environment variable SPLUNK_START_ARGS contains the '--accept-license' flag\n"
23-
printf "For example: docker run -e SPLUNK_START_ARGS=--accept-license splunk/splunk\n\n"
24-
printf "For additional information and examples, see the help: docker run -it splunk/splunk help\n"
23+
printf "For example: docker run -e SPLUNK_START_ARGS=--accept-license splunk/universalforwarder\n\n"
24+
printf "For additional information and examples, see the help: docker run -it splunk/universalforwarder help\n"
2525
exit 1
2626
fi
2727
}
@@ -35,6 +35,9 @@ trap teardown SIGINT SIGTERM
3535

3636
prep_ansible() {
3737
cd ${SPLUNK_ANSIBLE_HOME}
38+
if [ `whoami` == "${SPLUNK_USER}" ]; then
39+
sed -i -e "s,^become\\s*=.*,become = false," ansible.cfg
40+
fi
3841
if [[ "$DEBUG" == "true" ]]; then
3942
ansible-playbook --version
4043
python inventory/environ.py --write-to-file
@@ -52,42 +55,44 @@ watch_for_failure(){
5255
echo Ansible playbook complete, will begin streaming var/log/splunk/splunkd_stderr.log
5356
echo
5457
user_permission_change
55-
# Any crashes/errors while Splunk is running should get logged to splunkd_stderr.log and sent to the container's stdout
58+
if [ `whoami` != "${SPLUNK_USER}" ]; then
59+
RUN_AS_SPLUNK="sudo -u ${SPLUNK_USER}"
60+
fi
5661
# Any crashes/errors while Splunk is running should get logged to splunkd_stderr.log and sent to the container's stdout
5762
if [ -z "$SPLUNK_TAIL_FILE" ]; then
58-
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
63+
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
5964
else
60-
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
65+
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
6166
fi
6267
wait
6368
}
6469

6570
create_defaults() {
66-
createdefaults.py
71+
createdefaults.py
6772
}
6873

6974
start_and_exit() {
70-
if [ -z "$SPLUNK_PASSWORD" ]
71-
then
72-
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
73-
fi
75+
if [ -z "$SPLUNK_PASSWORD" ]
76+
then
77+
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
78+
fi
7479
sh -c "echo 'starting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
7580
setup
76-
prep_ansible
81+
prep_ansible
7782
ansible-playbook $ANSIBLE_EXTRA_FLAGS -i inventory/environ.py site.yml
7883
}
7984

8085
start() {
81-
trap teardown EXIT
86+
trap teardown EXIT
8287
start_and_exit
83-
watch_for_failure
88+
watch_for_failure
8489
}
8590

8691
restart(){
8792
trap teardown EXIT
8893
sh -c "echo 'restarting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
89-
prep_ansible
90-
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
94+
prep_ansible
95+
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
9196
ansible-playbook -i inventory/environ.py start.yml
9297
watch_for_failure
9398
}
@@ -123,7 +128,7 @@ Environment Variables:
123128
124129
125130
EOF
126-
exit 1
131+
exit 1
127132
}
128133

129134
case "$1" in
@@ -136,12 +141,12 @@ case "$1" in
136141
start_and_exit $@
137142
;;
138143
create-defaults)
139-
create_defaults
140-
;;
144+
create_defaults
145+
;;
141146
restart)
142-
shift
143-
restart $@
144-
;;
147+
shift
148+
restart $@
149+
;;
145150
no-provision)
146151
user_permission_change
147152
tail -n 0 -f /etc/hosts &

0 commit comments

Comments
 (0)