How to control and automate your Elgato Keylight from the command line
All code featured in this blog post can be found here on GitHub. Problem Have you ever wanted …
We’ve all been there. You do everything correct - you set your container to run as non root, you configure your application port as a dynamic port that can be changed at runtime, and then, when runtime comes, you run something like the following command:
docker run -e PORT=80 -p 80:80 --name my-app registry/image:tag` and you see this `listen tcp :80: bind: permission denied
The problem is simple, and so is the solution.
First, the problem is you’re trying to bind to a privileged port - port 80 - but your container is running as non root.
Second, the solution is to grant the necessary root capabilities to the process trying to bind to the privileged port. You can do that by using the setcap command available in the libcap package.
This will vary depending on how you have your Dockerfile setup.
If you are using a Debian based image, I believe the libcap package (libcap2-bin) is already installed so you just need to run
setcap 'cap_net_bind_service=+ep' /path-to-app-here
Note: If you are uisng a Go application with Debian and you receive an error like: standard_init_linux.go:211: exec user process caused "no such file or directory"
try adding ENV CGO_ENABLED=0
to your build stage. If you’re new to go, Cgo let’s Go packages call C code; setting CGO_ENABLED to “0” turns this functionality off, which fixes the above error. A full example of how this would look in your Dockerfile can be found here. Though, honestly, you should use Alpine for runtime in a multistage Dockerfile and leave your debian base image to your build stage. Alpine won’t need this extra build ENV since Alpine images are musl libc based.
If you are uisng Alpine, which you probably should be, you will need to install the libcap package before you can use the setcap
command. You can install and call setcap with one line: RUN apk add libcap && setcap 'cap_net_bind_service=+ep' /path-to-app-here
.
Once added in the order that makes sense, your Dockerfile might look some what like mine:
FROM golang:1.14.2-alpine3.11 as builder
RUN apk update && apk add --no-cache git
WORKDIR $GOPATH/src/croc-hunter/
ENV GOPATH /go
COPY croc-hunter.go /go/src/croc-hunter/
COPY static/ static/
RUN go get -d -v
RUN go build -o /go/bin/croc-hunter
FROM alpine AS final
WORKDIR /app
COPY static/ static/
COPY --from=builder /go/bin/croc-hunter /app/croc-hunter
RUN apk add libcap && setcap 'cap_net_bind_service=+ep' /app/croc-hunter
USER 1000
EXPOSE ${PORT}
CMD ["/app/croc-hunter"]
Once you rebuild your Dockefile with the updated setcap command, retry your docker run
command you should see success:
You can see this in action in my latest YouTube Video (TS 6:35), just click the image below!