Skip to content
Merged
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
87 changes: 87 additions & 0 deletions docs/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
## Security ##
This section will cover various security considerations when using the Splunk Enterprise and Universal Forwarder containers.

### Startup Users ###

The Splunk Enterprise and Universal Forwarder containers may be started using one of the following three user accounts:

* `splunk` (most secure): This user has no privileged access and cannot use `sudo` to change to another user account.
It is a member of the `ansible` group, which enables it to run the embedded playbooks at startup. When using the
`splunk` user, all processes will run as this user. Note that you must set the `SPLUNK_HOME_OWNERSHIP_ENFORCEMENT`
environment variable to `false` when starting as this user. ***Recommended for production***

* `ansible` (middle ground): This user is a member of the `sudo` group and able to execute `sudo` commands without a
password. It uses privileged access at startup only to perform certain actions which cannot be performed by regular
users (see below). After startup, `sudo` access will automatically be removed from the `ansible` user if the
environment variable `STEPDOWN_ANSIBLE_USER` is set to `true`. ***This is the default user account***

* `root` (least secure): This is a privileged user running with UID of `0`. Some customers may want to use this for
forwarder processes that require access to log files which cannot be read by any other user. ***This is not recommended***

### After Startup ###

By default, the primary Splunk processes will always run as the unprivileged user and group `splunk`,
irregardless of which user account the containers are started with. You can override this by changing the following:

* User: `splunk.user` variable in your `default.yml` template, or the `SPLUNK_USER` environment variable
* Group: `splunk.group` variable in your `default.yml` template, or the `SPLUNK_GROUP` environment variable

Note that the containers are built with the `splunk` user having UID `41812` and the `splunk` group having GID `41812`.

You may want to override these settings to ensure that Splunk forwarder processes have access to read your log files.
For example, you can ensure that all processes run as `root` by starting as the `root` user with the environment
variable `SPLUNK_USER` also set to `root` (this is not recommended).

### Privileged Features ###

Certain features supported by the Splunk Enterprise and Universal Forwarder containers require that they are started
with privileged access using either the `ansible` or `root` user accounts.

#### Splunk Home Ownership ####

By default, at startup the containers will ensure that all files located under the `SPLUNK_HOME` directory
(`/opt/splunk`) are owned by user `splunk` and group `splunk`. This helps to ensure that the Splunk processes are
able to read and write any external volumes mounted for `/opt/splunk/etc` and `/opt/splunk/var`. While all supported
versions of the docker engine will automatically set proper ownership for these volumes, external orchestration systems
typically will require extra steps.

If you know that this step is unnecessary, you can disable it by setting the `SPLUNK_HOME_OWNERSHIP_ENFORCEMENT`
environment variable to `false`. Note that this must be disabled when starting containers with the `splunk` user
account.

#### Package Installation ####

The `JAVA_VERSION` environment variable can be used to automatically install OpenJDK at startup time. This feature
requires starting as a privileged user account.

### Kubernetes Users ###

For Kubernetes, we recommend using the `fsGroup` [Security Context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
to ensure that all Pods are able to write to your Persistent Volumes. For example:

```
apiVersion: v1
kind: Pod
metadata:
name: example-splunk-pod
spec:
securityContext:
runAsUser: 41812
fsGroup: 41812
containers:
name: example-splunk-container
image: splunk/splunk
env:
- name: SPLUNK_HOME_OWNERSHIP_ENFORCEMENT
value: "false"
...
```

This can be used to create a Splunk Enterprise Pod running as the unprivileged `splunk` user which is able to securely
read and write from any Persistent Volumes that are created for it.

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)
to run Pods with the above Security Context:
```
oc adm policy add-scc-to-user nonroot default
```
8 changes: 6 additions & 2 deletions splunk/common-files/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,15 @@ RUN sed -i -e 's/%sudo\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%sudo ALL=NOPASSWD:ALL/g' /
&& groupadd -r ${ANSIBLE_GROUP} \
&& useradd -r -m -g ${ANSIBLE_GROUP} ${ANSIBLE_USER} \
&& usermod -aG sudo ${ANSIBLE_USER} \
&& usermod -aG ${ANSIBLE_GROUP} ${SPLUNK_USER} \
# 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".
&& mkdir ${CONTAINER_ARTIFACT_DIR} \
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} $CONTAINER_ARTIFACT_DIR \
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} ${CONTAINER_ARTIFACT_DIR} \
&& chmod -R 775 ${CONTAINER_ARTIFACT_DIR} \
&& chmod -R 555 ${SPLUNK_ANSIBLE_HOME} \
&& chmod -R 777 ${CONTAINER_ARTIFACT_DIR} \
&& chgrp ${ANSIBLE_GROUP} ${SPLUNK_ANSIBLE_HOME} ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
&& chmod 775 ${SPLUNK_ANSIBLE_HOME} \
&& chmod 664 ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
&& chmod 755 /sbin/entrypoint.sh /sbin/createdefaults.py /sbin/checkstate.sh

USER ${ANSIBLE_USER}
Expand Down
46 changes: 26 additions & 20 deletions splunk/common-files/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ trap teardown SIGINT SIGTERM

prep_ansible() {
cd ${SPLUNK_ANSIBLE_HOME}
if [ `whoami` == "${SPLUNK_USER}" ]; then
sed -i -e "s,^become\\s*=.*,become = false," ansible.cfg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small thing here that I don't quite like is that our ansible.cfg as shown in the repo is not actually representative of the ansible.cfg during execution under a different user. But I suppose this is a necessary evil at the moment so as long as it's documented, it should be alright.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was hoping that become_user when you are that user is a noop, but unfortuantely that is not the case. Without explicitly disabling it here, ansible bombs out at startup with complaints that it cannot use sudo without a password.

fi
if [[ "$DEBUG" == "true" ]]; then
ansible-playbook --version
python inventory/environ.py --write-to-file
Expand All @@ -53,34 +56,37 @@ watch_for_failure(){
echo Ansible playbook complete, will begin streaming var/log/splunk/splunkd_stderr.log
echo
user_permission_change
if [ `whoami` != "${SPLUNK_USER}" ]; then
RUN_AS_SPLUNK="sudo -u ${SPLUNK_USER}"
fi
# Any crashes/errors while Splunk is running should get logged to splunkd_stderr.log and sent to the container's stdout
if [ -z "$SPLUNK_TAIL_FILE" ]; then
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
else
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
fi
wait
}

create_defaults() {
createdefaults.py
createdefaults.py
}

start_and_exit() {
if [ -z "$SPLUNK_PASSWORD" ]
then
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
fi
if [ -z "$SPLUNK_PASSWORD" ]
then
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
fi
sh -c "echo 'starting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
setup
prep_ansible
prep_ansible
ansible-playbook $ANSIBLE_EXTRA_FLAGS -i inventory/environ.py site.yml
}

start() {
trap teardown EXIT
trap teardown EXIT
start_and_exit
watch_for_failure
watch_for_failure
}

configure_multisite() {
Expand All @@ -89,17 +95,17 @@ configure_multisite() {
}

restart(){
trap teardown EXIT
trap teardown EXIT
sh -c "echo 'restarting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
prep_ansible
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
prep_ansible
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
ansible-playbook -i inventory/environ.py start.yml
watch_for_failure
}

user_permission_change(){
if [[ "$STEPDOWN_ANSIBLE_USER" == "true" ]]; then
bash -c "sudo deluser -q ansible sudo"
bash -c "sudo deluser -q ansible sudo"
fi
}

Expand Down Expand Up @@ -140,7 +146,7 @@ Examples:
* 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

EOF
exit 1
exit 1
}

case "$1" in
Expand All @@ -157,12 +163,12 @@ case "$1" in
configure_multisite $0
;;
create-defaults)
create_defaults
;;
create_defaults
;;
restart)
shift
restart $@
;;
shift
restart $@
;;
no-provision)
user_permission_change
tail -n 0 -f /etc/hosts &
Expand Down
7 changes: 6 additions & 1 deletion uf/common-files/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,15 @@ RUN \
&& groupadd -r ${ANSIBLE_GROUP} \
&& useradd -r -m -g ${ANSIBLE_GROUP} ${ANSIBLE_USER} \
&& usermod -aG sudo ${ANSIBLE_USER} \
&& usermod -aG ${ANSIBLE_GROUP} ${SPLUNK_USER} \
# 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".
&& mkdir ${CONTAINER_ARTIFACT_DIR} \
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} $CONTAINER_ARTIFACT_DIR \
&& chown -R ${ANSIBLE_USER}:${ANSIBLE_GROUP} ${CONTAINER_ARTIFACT_DIR} \
&& chmod -R 775 ${CONTAINER_ARTIFACT_DIR} \
&& chmod -R 555 ${SPLUNK_ANSIBLE_HOME} \
&& chgrp ${ANSIBLE_GROUP} ${SPLUNK_ANSIBLE_HOME} ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
&& chmod 775 ${SPLUNK_ANSIBLE_HOME} \
&& chmod 664 ${SPLUNK_ANSIBLE_HOME}/ansible.cfg \
&& chmod 755 /sbin/entrypoint.sh /sbin/createdefaults.py /sbin/checkstate.sh

USER ${ANSIBLE_USER}
Expand Down
47 changes: 26 additions & 21 deletions uf/common-files/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ setup() {
# Check if the user accepted the license
if [[ "$SPLUNK_START_ARGS" != *"--accept-license"* ]]; then
printf "License not accepted, please ensure the environment variable SPLUNK_START_ARGS contains the '--accept-license' flag\n"
printf "For example: docker run -e SPLUNK_START_ARGS=--accept-license splunk/splunk\n\n"
printf "For additional information and examples, see the help: docker run -it splunk/splunk help\n"
printf "For example: docker run -e SPLUNK_START_ARGS=--accept-license splunk/universalforwarder\n\n"
printf "For additional information and examples, see the help: docker run -it splunk/universalforwarder help\n"
exit 1
fi
}
Expand All @@ -35,6 +35,9 @@ trap teardown SIGINT SIGTERM

prep_ansible() {
cd ${SPLUNK_ANSIBLE_HOME}
if [ `whoami` == "${SPLUNK_USER}" ]; then
sed -i -e "s,^become\\s*=.*,become = false," ansible.cfg
fi
if [[ "$DEBUG" == "true" ]]; then
ansible-playbook --version
python inventory/environ.py --write-to-file
Expand All @@ -52,42 +55,44 @@ watch_for_failure(){
echo Ansible playbook complete, will begin streaming var/log/splunk/splunkd_stderr.log
echo
user_permission_change
# Any crashes/errors while Splunk is running should get logged to splunkd_stderr.log and sent to the container's stdout
if [ `whoami` != "${SPLUNK_USER}" ]; then
RUN_AS_SPLUNK="sudo -u ${SPLUNK_USER}"
fi
# Any crashes/errors while Splunk is running should get logged to splunkd_stderr.log and sent to the container's stdout
if [ -z "$SPLUNK_TAIL_FILE" ]; then
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_HOME}/var/log/splunk/splunkd_stderr.log &
else
sudo -u ${SPLUNK_USER} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
${RUN_AS_SPLUNK} tail -n 0 -f ${SPLUNK_TAIL_FILE} &
fi
wait
}

create_defaults() {
createdefaults.py
createdefaults.py
}

start_and_exit() {
if [ -z "$SPLUNK_PASSWORD" ]
then
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
fi
if [ -z "$SPLUNK_PASSWORD" ]
then
echo "WARNING: No password ENV var. Stack may fail to provision if splunk.password is not set in ENV or a default.yml"
fi
sh -c "echo 'starting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
setup
prep_ansible
prep_ansible
ansible-playbook $ANSIBLE_EXTRA_FLAGS -i inventory/environ.py site.yml
}

start() {
trap teardown EXIT
trap teardown EXIT
start_and_exit
watch_for_failure
watch_for_failure
}

restart(){
trap teardown EXIT
sh -c "echo 'restarting' > ${CONTAINER_ARTIFACT_DIR}/splunk-container.state"
prep_ansible
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
prep_ansible
${SPLUNK_HOME}/bin/splunk stop 2>/dev/null || true
ansible-playbook -i inventory/environ.py start.yml
watch_for_failure
}
Expand Down Expand Up @@ -123,7 +128,7 @@ Environment Variables:


EOF
exit 1
exit 1
}

case "$1" in
Expand All @@ -136,12 +141,12 @@ case "$1" in
start_and_exit $@
;;
create-defaults)
create_defaults
;;
create_defaults
;;
restart)
shift
restart $@
;;
shift
restart $@
;;
no-provision)
user_permission_change
tail -n 0 -f /etc/hosts &
Expand Down