Skip to content

Commit

Permalink
Docker Image enhancement and better CLI support. (#1603)
Browse files Browse the repository at this point in the history
## THIS PR INCLUDES BREAKING CHANGES
- Dockerfile now uses `app` user instead of `root`

#### Why?

A few Geo-Mesh users report to POKTscan about an issue with the new
image after we adopt the one on the pokt-network/pocket-core repository
on the latest RC.

They report that this image is using `root` as the user which is
recommended to be avoided. There are a lot of blogs and documentation
about this,
[here](https://docs.bitnami.com/tutorials/why-non-root-containers-are-important-for-security)
one of them from a well-known docker image user/company.

Also, we detected a few things that could be enhanced on both, entry
point and docker context.

The problem with having a public image using root right now is that
pocket binary generates folders and files that now belong to the `root`
user, so they will need to modify those permissions to belong to the
proper `app` user and group. To this, I added another optional entry
point that could be used once to fix the permission issue and then start
the container as before.


[Here](https://github.com/pokt-network/pocket-core/pull/1603/files#diff-8a9d880ecb3d20b3cfe8def4da1bd200fcbd44131caee95ea699a34faf9cfd6fR19)
you can see how to use it with docker-compose or docker

#### Changes:

- Modifications to Dockerfile allow the container to run as `app` user
instead of `root`.
- Added a new shell script named fix_permissions.sh to fix ownership
issues related to running containers.
- Updated Dockerfile to use this new script.
- Added a .dockerignore file to help maintain a cleaner Docker build
context, excluding unnecessary files.
- Modifications to `entrypoint.sh` allows the user to run all its
internal commands with the proper `--datadir` param. Now properly handle
the start command when `--keybase=false` is sent. Also, allow the user
to pass the `--datadir` as an env variable to omit it on the start
command.
  • Loading branch information
jorgecuesta committed May 20, 2024
1 parent 21d70e9 commit 33b28c1
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 17 deletions.
16 changes: 16 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# this is not need on context
.circleci
# only workflow files that could be needed on the image should be added to the context
!.github/workflows
# ide
.editorconfig
# doc files
doc
docs
# yml/yaml
**/*.yaml
**/*.yml
# markdown
**/*.md
# images
**/*.png
24 changes: 22 additions & 2 deletions .github/workflows/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,31 @@ ADD . .
RUN go build -o pocket app/cmd/pocket_core/main.go

FROM alpine
RUN apk add --update --no-cache expect bash leveldb-dev tzdata && cp /usr/share/zoneinfo/America/New_York /etc/localtime \
RUN apk add --update --no-cache expect bash leveldb-dev tzdata curl \
&& cp /usr/share/zoneinfo/America/New_York /etc/localtime \
&& addgroup --gid 1001 -S app \
&& adduser --uid 1005 -S -G app app

COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /build/pocket /bin/pocket
COPY .github/workflows/entrypoint.sh /home/app/entrypoint.sh
RUN chown -R app /bin/pocket && mkdir -p /home/app/.pocket/config && chown -R app /home/app/.pocket
# allow users to run the container as root and fix the permissions in an easy way
# this could be used as a one time run entrypoint.
# e.g:
# docker-compose:
# entrypoint: [ "/home/app/change_datadir_ownership_to_app.sh", "/home/app/.pocket"]
# user: root
# docker:
# --entrypoint="/home/app/change_datadir_ownership_to_app.sh /home/app/.pocket" -u root
COPY .github/workflows/change_datadir_ownership_to_app.sh /home/app/change_datadir_ownership_to_app.sh

RUN chmod +x /home/app/entrypoint.sh && \
chmod +x /home/app/change_datadir_ownership_to_app.sh && \
chown -R app /bin/pocket \
&& mkdir -p /home/app/.pocket/config \
&& chown -R app /home/app/.pocket

# run the container as app user instead of root
USER app

ENTRYPOINT ["/usr/bin/expect", "/home/app/entrypoint.sh"]
13 changes: 13 additions & 0 deletions .github/workflows/change_datadir_ownership_to_app.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh
datadir=$1
# Changing the permissions is necessary here because previous versions of our Dockerfile
# did not specify `app` as the user, so Docker defaulted to `root`.
# This script facilitates transition for those who have not specified the `app` user at the start of the container.
# It changes the ownership to the proper user and group (`app:app`), as declared in the Dockerfile.
# The specific ownership by user 'app' and group 'app' is required to ensure that the `app` user
# specified in the Dockerfile will have full access to the relevant directory.
echo "Attempting to fix ${datadir} permissions to be owned by app:app"
chown -R app:app $datadir
echo "${datadir} permissions applied."
echo "Please turn off entrypoint override and ensure you are using user `app` or user `1005` when start container."
exit 0
72 changes: 57 additions & 15 deletions .github/workflows/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ trap graceful_exit {SIGINT SIGTERM}
set command $argv
set timeout -1

# Create work dir
spawn sh -c "mkdir -p /home/app/.pocket/config"
expect eof

# Pull variables from env if set
set defaultDatadir "/home/app/.pocket"
set datadir $defaultDatadir
catch {set datadir $env(POCKET_CORE_DATADIR)}
set datadirParam "--datadir=${datadir}"

set genesis ""
catch {set genesis $env(POCKET_CORE_GENESIS)}

Expand All @@ -26,42 +27,83 @@ catch {set chains $env(POCKET_CORE_CHAINS)}
set config ""
catch {set config $env(POCKET_CORE_CONFIG)}

set core_key ""
catch {set core_key $env(POCKET_CORE_KEY)}

set core_passphrase ""
catch {set core_passphrase $env(POCKET_CORE_PASSPHRASE)}

# Create dynamic config files
if {$genesis != ""} {
set genesis_file [open /home/app/.pocket/config/genesis.json w]
set genesis_file [open "{$datadir}/config/genesis.json" w]
puts $genesis_file $genesis
close $genesis_file
send_user "GENESIS loaded from env\n"
}
if {$chains != ""} {
set chains_file [open /home/app/.pocket/config/chains.json w]
set chains_file [open "${datadir}/config/chains.json" w]
puts $chains_file $chains
close $chains_file
send_user "CHAINS loaded from env\n"
}
if {$config != ""} {
set config_file [open /home/app/.pocket/config/config.json w]
set config_file [open "${datadir}/config/config.json" w]
puts $config_file $config
close $config_file
send_user "CONFIG loaded from env\n"
}

# If key isn't passed in, start the node
if { $env(POCKET_CORE_KEY) eq "" } {
if {[regexp -nocase "datadir=" $command] && ![regexp -nocase "datadir=${datadir}" $command]} {
send_user "WARNING: --datadir provided with a different path than the one defined in the Dockerfile. This could lead to errors using pocket CLI commands.\n"
} elseif {![regexp -nocase "datadir=" $command]} {
send_user "INFO: param --datadir was not provided; attaching ${datadirParam} on every command\n"
set command "${command} ${datadirParam}"
}

if {![regexp -nocase "datadir=${defaultDatadir}" $command]} {
send_user "WARNING: --datadir is not the default one
Please review:
1. Mount your config folder to the same path you specify in --datadir
2. Review your config.json to ensure the following configs match the value of --datadir
2.1. tendermint_config.RootDir
2.2. RPC.RootDir
2.3. P2P.RootDir
2.4. Mempool.RootDir
2.5. Consensus.RootDir
2.6. pocket_config.RootDir
"
}

proc check_passphrase {str} {
send_user "checking passphrase: ${str}\n"
if {$str == ""} {
send_user "missing POCKET_CORE_PASSPHRASE environment variable"
exit 1
}
}

# if not --keybase=false
# e.g. "pocket start --keybase=false --mainnet --datadir=/home/app/.pocket/"
if {[regexp -nocase "keybase=false" $command]} {
spawn sh -c "$command"
} elseif { $core_key eq "" } {
# If key isn't passed in, start the node
check_passphrase $core_passphrase
log_user 0
spawn sh -c "$command"
send -- "$env(POCKET_CORE_PASSPHRASE)\n"
send -- "$core_passphrase\n"
log_user 1
} else {
# If key is passed in, load it into the local accounts
check_passphrase $core_passphrase
# If key is passed in, load it into the local accounts
log_user 0
spawn pocket accounts import-raw $env(POCKET_CORE_KEY)
spawn pocket accounts import-raw $datadirParam $core_key
sleep 1
send -- "$env(POCKET_CORE_PASSPHRASE)\n"
send -- "$core_passphrase\n"
expect eof
spawn sh -c "pocket accounts set-validator `pocket accounts list | cut -d' ' -f2- `"
spawn sh -c "pocket accounts set-validator ${datadirParam} `pocket accounts list ${datadirParam} | cut -d' ' -f2- `"
sleep 1
send -- "$env(POCKET_CORE_PASSPHRASE)\n"
send -- "$core_passphrase\n"
expect eof
log_user 1
spawn sh -c "$command"
Expand Down

0 comments on commit 33b28c1

Please sign in to comment.