Add support for customizable user (#7569)

master
Harshavardhana 6 years ago committed by Nitish Tiwari
parent 1008c2c069
commit 91ceae23d0
  1. 1
      .gitignore
  2. 4
      Dockerfile
  3. 7
      Dockerfile.dev
  4. 9
      Dockerfile.release
  5. 1
      Makefile
  6. 10
      cmd/endpoint-ellipses.go
  7. 4
      cmd/gateway-main.go
  8. 6
      cmd/main.go
  9. 5
      cmd/server-main.go
  10. 136
      dockerscripts/check-user.go
  11. 48
      dockerscripts/docker-entrypoint.sh
  12. 15
      dockerscripts/healthcheck.go
  13. 29
      docs/docker/README.md

1
.gitignore vendored

@ -24,3 +24,4 @@ stage/
.sia_temp/ .sia_temp/
config.json config.json
healthcheck healthcheck
check-user

@ -10,7 +10,8 @@ RUN \
apk add --no-cache git && \ apk add --no-cache git && \
git clone https://github.com/minio/minio && cd minio && \ git clone https://github.com/minio/minio && cd minio && \
go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)" && \ go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)" && \
cd dockerscripts; go build -ldflags "-s -w" -o /usr/bin/healthcheck healthcheck.go cd dockerscripts; go build -tags kqueue -ldflags "-s -w" -o /usr/bin/healthcheck healthcheck.go && \
go build -tags kqueue -ldflags "-s -w" -o /usr/bin/check-user check-user.go
FROM alpine:3.9 FROM alpine:3.9
@ -22,6 +23,7 @@ EXPOSE 9000
COPY --from=0 /go/bin/minio /usr/bin/minio COPY --from=0 /go/bin/minio /usr/bin/minio
COPY --from=0 /usr/bin/healthcheck /usr/bin/healthcheck COPY --from=0 /usr/bin/healthcheck /usr/bin/healthcheck
COPY --from=0 /usr/bin/check-user /usr/bin/check-user
COPY dockerscripts/docker-entrypoint.sh /usr/bin/ COPY dockerscripts/docker-entrypoint.sh /usr/bin/
RUN \ RUN \

@ -2,7 +2,7 @@ FROM alpine:3.9
LABEL maintainer="MinIO Inc <dev@min.io>" LABEL maintainer="MinIO Inc <dev@min.io>"
COPY dockerscripts/docker-entrypoint.sh dockerscripts/healthcheck /usr/bin/ COPY dockerscripts/docker-entrypoint.sh dockerscripts/healthcheck dockerscripts/check-user /usr/bin/
COPY minio /usr/bin/ COPY minio /usr/bin/
ENV MINIO_UPDATE off ENV MINIO_UPDATE off
@ -10,11 +10,12 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
MINIO_SECRET_KEY_FILE=secret_key MINIO_SECRET_KEY_FILE=secret_key
RUN \ RUN \
apk add --no-cache ca-certificates 'curl>7.61.0' && \ apk add --no-cache ca-certificates 'curl>7.61.0' 'su-exec>=0.2' && \
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf && \ echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf && \
chmod +x /usr/bin/minio && \ chmod +x /usr/bin/minio && \
chmod +x /usr/bin/docker-entrypoint.sh && \ chmod +x /usr/bin/docker-entrypoint.sh && \
chmod +x /usr/bin/healthcheck chmod +x /usr/bin/healthcheck && \
chmod +x /usr/bin/check-user
EXPOSE 9000 EXPOSE 9000

@ -7,13 +7,15 @@ ENV GO111MODULE on
RUN \ RUN \
apk add --no-cache git && \ apk add --no-cache git && \
git clone https://github.com/minio/minio && cd minio/dockerscripts && \ git clone https://github.com/minio/minio && cd minio/dockerscripts && \
go build -ldflags "-s -w" -o /usr/bin/healthcheck healthcheck.go go build -tags kqueue -ldflags "-s -w" -o /usr/bin/healthcheck healthcheck.go && \
go build -tags kqueue -ldflags "-s -w" -o /usr/bin/check-user check-user.go
FROM alpine:3.9 FROM alpine:3.9
LABEL maintainer="MinIO Inc <dev@min.io>" LABEL maintainer="MinIO Inc <dev@min.io>"
COPY --from=0 /usr/bin/healthcheck /usr/bin/healthcheck COPY --from=0 /usr/bin/healthcheck /usr/bin/healthcheck
COPY --from=0 /usr/bin/check-user /usr/bin/check-user
COPY dockerscripts/docker-entrypoint.sh /usr/bin/ COPY dockerscripts/docker-entrypoint.sh /usr/bin/
ENV MINIO_UPDATE off ENV MINIO_UPDATE off
@ -21,12 +23,13 @@ ENV MINIO_ACCESS_KEY_FILE=access_key \
MINIO_SECRET_KEY_FILE=secret_key MINIO_SECRET_KEY_FILE=secret_key
RUN \ RUN \
apk add --no-cache ca-certificates 'curl>7.61.0' && \ apk add --no-cache ca-certificates 'curl>7.61.0' 'su-exec>=0.2' && \
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf && \ echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf && \
curl https://dl.min.io/server/minio/release/linux-amd64/minio > /usr/bin/minio && \ curl https://dl.min.io/server/minio/release/linux-amd64/minio > /usr/bin/minio && \
chmod +x /usr/bin/minio && \ chmod +x /usr/bin/minio && \
chmod +x /usr/bin/docker-entrypoint.sh && \ chmod +x /usr/bin/docker-entrypoint.sh && \
chmod +x /usr/bin/healthcheck chmod +x /usr/bin/healthcheck && \
chmod +x /usr/bin/check-user
EXPOSE 9000 EXPOSE 9000

@ -73,6 +73,7 @@ build: checks
@echo "Building minio binary to './minio'" @echo "Building minio binary to './minio'"
@GO111MODULE=on GOFLAGS="" CGO_ENABLED=0 go build -tags kqueue --ldflags $(BUILD_LDFLAGS) -o $(PWD)/minio 1>/dev/null @GO111MODULE=on GOFLAGS="" CGO_ENABLED=0 go build -tags kqueue --ldflags $(BUILD_LDFLAGS) -o $(PWD)/minio 1>/dev/null
@GO111MODULE=on GOFLAGS="" CGO_ENABLED=0 go build -tags kqueue --ldflags $(BUILD_LDFLAGS) -o $(PWD)/dockerscripts/healthcheck $(PWD)/dockerscripts/healthcheck.go 1>/dev/null @GO111MODULE=on GOFLAGS="" CGO_ENABLED=0 go build -tags kqueue --ldflags $(BUILD_LDFLAGS) -o $(PWD)/dockerscripts/healthcheck $(PWD)/dockerscripts/healthcheck.go 1>/dev/null
@GO111MODULE=on GOFLAGS="" CGO_ENABLED=0 go build -tags kqueue --ldflags $(BUILD_LDFLAGS) -o $(PWD)/dockerscripts/check-user $(PWD)/dockerscripts/check-user.go 1>/dev/null
docker: build docker: build
@docker build -t $(TAG) . -f Dockerfile.dev @docker build -t $(TAG) . -f Dockerfile.dev

@ -213,12 +213,12 @@ func parseEndpointSet(args ...string) (ep endpointSet, err error) {
return ep, nil return ep, nil
} }
// Parses all ellipses input arguments, expands them into corresponding // GetAllSets - parses all ellipses input arguments, expands them into
// list of endpoints chunked evenly in accordance with a specific // corresponding list of endpoints chunked evenly in accordance with a
// set size. // specific set size.
// For example: {1...64} is divided into 4 sets each of size 16. // For example: {1...64} is divided into 4 sets each of size 16.
// This applies to even distributed setup syntax as well. // This applies to even distributed setup syntax as well.
func getAllSets(args ...string) ([][]string, error) { func GetAllSets(args ...string) ([][]string, error) {
if len(args) == 0 { if len(args) == 0 {
return nil, errInvalidArgument return nil, errInvalidArgument
} }
@ -266,7 +266,7 @@ func getAllSets(args ...string) ([][]string, error) {
// CreateServerEndpoints - validates and creates new endpoints from input args, supports // CreateServerEndpoints - validates and creates new endpoints from input args, supports
// both ellipses and without ellipses transparently. // both ellipses and without ellipses transparently.
func createServerEndpoints(serverAddr string, args ...string) (string, EndpointList, SetupType, int, int, error) { func createServerEndpoints(serverAddr string, args ...string) (string, EndpointList, SetupType, int, int, error) {
setArgs, err := getAllSets(args...) setArgs, err := GetAllSets(args...)
if err != nil { if err != nil {
return serverAddr, nil, -1, 0, 0, err return serverAddr, nil, -1, 0, 0, err
} }

@ -41,14 +41,14 @@ var (
gatewayCmd = cli.Command{ gatewayCmd = cli.Command{
Name: "gateway", Name: "gateway",
Usage: "start object storage gateway", Usage: "start object storage gateway",
Flags: append(serverFlags, globalFlags...), Flags: append(ServerFlags, GlobalFlags...),
HideHelpCommand: true, HideHelpCommand: true,
} }
) )
// RegisterGatewayCommand registers a new command for gateway. // RegisterGatewayCommand registers a new command for gateway.
func RegisterGatewayCommand(cmd cli.Command) error { func RegisterGatewayCommand(cmd cli.Command) error {
cmd.Flags = append(append(cmd.Flags, append(cmd.Flags, serverFlags...)...), globalFlags...) cmd.Flags = append(append(cmd.Flags, append(cmd.Flags, ServerFlags...)...), GlobalFlags...)
gatewayCmd.Subcommands = append(gatewayCmd.Subcommands, cmd) gatewayCmd.Subcommands = append(gatewayCmd.Subcommands, cmd)
return nil return nil
} }

@ -27,8 +27,8 @@ import (
"github.com/minio/minio/pkg/words" "github.com/minio/minio/pkg/words"
) )
// global flags for minio. // GlobalFlags - global flags for minio.
var globalFlags = []cli.Flag{ var GlobalFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "config-dir, C", Name: "config-dir, C",
Value: defaultConfigDir.Get(), Value: defaultConfigDir.Get(),
@ -131,7 +131,7 @@ func newApp(name string) *cli.App {
app.Version = Version app.Version = Version
app.Usage = "Cloud Storage Server." app.Usage = "Cloud Storage Server."
app.Description = `MinIO is an Amazon S3 compatible object storage server. Use it to store photos, videos, VMs, containers, log files, or any blob of data as objects.` app.Description = `MinIO is an Amazon S3 compatible object storage server. Use it to store photos, videos, VMs, containers, log files, or any blob of data as objects.`
app.Flags = globalFlags app.Flags = GlobalFlags
app.HideVersion = true // Hide `--version` flag, we already have `minio version`. app.HideVersion = true // Hide `--version` flag, we already have `minio version`.
app.HideHelpCommand = true // Hide `help, h` command, we already have `minio --help`. app.HideHelpCommand = true // Hide `help, h` command, we already have `minio --help`.
app.Commands = commands app.Commands = commands

@ -38,7 +38,8 @@ func init() {
logger.RegisterUIError(fmtError) logger.RegisterUIError(fmtError)
} }
var serverFlags = []cli.Flag{ // ServerFlags - server command specific flags
var ServerFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "address", Name: "address",
Value: ":" + globalMinioDefaultPort, Value: ":" + globalMinioDefaultPort,
@ -49,7 +50,7 @@ var serverFlags = []cli.Flag{
var serverCmd = cli.Command{ var serverCmd = cli.Command{
Name: "server", Name: "server",
Usage: "start object storage server", Usage: "start object storage server",
Flags: append(serverFlags, globalFlags...), Flags: append(ServerFlags, GlobalFlags...),
Action: serverMain, Action: serverMain,
CustomHelpTemplate: `NAME: CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}} {{.HelpName}} - {{.Usage}}

@ -0,0 +1,136 @@
// +build ignore
/*
* MinIO Cloud Storage, (C) 2019 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"log"
"os"
"os/exec"
"os/user"
"syscall"
"github.com/minio/cli"
minio "github.com/minio/minio/cmd"
)
var defaultUserGroup string
func init() {
username := os.Getenv("MINIO_USERNAME")
groupname := os.Getenv("MINIO_GROUPNAME")
defaultUserGroup = username + ":" + groupname
}
func getUserGroup(path string) (string, error) {
fi, err := os.Stat(minio.PathJoin(path, ".minio.sys"))
if err != nil {
// Fresh directory we should default to what was requested by user.
if os.IsNotExist(err) {
cmd := exec.Command("chown", "-R", defaultUserGroup, path)
if err = cmd.Run(); err != nil {
return "", err
}
return defaultUserGroup, nil
}
return "", err
}
stat, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
// Unable to figure out uid/gid, default to defaultUserGroup
return defaultUserGroup, nil
}
u, err := user.LookupId(fmt.Sprintf("%d", stat.Uid))
if err != nil {
return fmt.Sprintf("%d:%d", stat.Uid, stat.Gid), nil
}
g, err := user.LookupGroupId(fmt.Sprintf("%d", stat.Gid))
if err != nil {
return fmt.Sprintf("%d:%d", stat.Uid, stat.Gid), nil
}
return fmt.Sprintf("%s:%s", u.Username, g.Name), nil
}
func main() {
app := cli.NewApp()
app.Flags = append(minio.ServerFlags, minio.GlobalFlags...)
app.Action = func(ctx *cli.Context) {
// Fetch address option
serverAddr := ctx.GlobalString("address")
if serverAddr == "" || serverAddr == ":9000" {
serverAddr = ctx.String("address")
}
if ctx.Args().First() == "help" {
cli.ShowCommandHelpAndExit(ctx, "check-user", 1)
}
if ctx.Args().First() != "minio" {
cli.ShowCommandHelpAndExit(ctx, "check-user", 1)
}
args := cli.Args(ctx.Args().Tail())
if !args.Present() {
cli.ShowCommandHelpAndExit(ctx, "check-user", 1)
}
var ug string
var err error
switch args.First() {
case "gateway":
args = cli.Args(args.Tail())
if args.First() != "nas" {
fmt.Println(defaultUserGroup)
return
}
args = cli.Args(args.Tail())
if args.First() == "" {
fmt.Println("")
return
}
ug, err = getUserGroup(args.First())
if err != nil {
log.Fatalln(err)
}
case "server":
var setArgs [][]string
setArgs, err = minio.GetAllSets(args.Tail()...)
if err != nil {
log.Fatalln(err)
}
var endpoints minio.EndpointList
_, endpoints, _, err = minio.CreateEndpoints(serverAddr, setArgs...)
if err != nil {
log.Fatalln(err)
}
for _, endpoint := range endpoints {
if !endpoint.IsLocal {
continue
}
ug, err = getUserGroup(endpoint.Path)
if err != nil {
log.Fatalln(err)
}
break
}
default:
cli.ShowCommandHelpAndExit(ctx, "check-user", 1)
}
fmt.Println(ug)
}
if err := app.Run(os.Args); err != nil {
log.Fatalln(err)
}
}

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# MinIO Cloud Storage, (C) 2017 MinIO, Inc. # MinIO Cloud Storage, (C) 2019 MinIO, Inc.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -15,6 +15,9 @@
# limitations under the License. # limitations under the License.
# #
export MINIO_USERNAME=${MINIO_USERNAME:-"minio"}
export MINIO_GROUPNAME=${MINIO_GROUPNAME:-"minio"}
# If command starts with an option, prepend minio. # If command starts with an option, prepend minio.
if [ "${1}" != "minio" ]; then if [ "${1}" != "minio" ]; then
if [ -n "${1}" ]; then if [ -n "${1}" ]; then
@ -24,20 +27,47 @@ fi
## Look for docker secrets in default documented location. ## Look for docker secrets in default documented location.
docker_secrets_env() { docker_secrets_env() {
local ACCESS_KEY_FILE="/run/secrets/$MINIO_ACCESS_KEY_FILE" ACCESS_KEY_FILE="/run/secrets/$MINIO_ACCESS_KEY_FILE"
local SECRET_KEY_FILE="/run/secrets/$MINIO_SECRET_KEY_FILE" SECRET_KEY_FILE="/run/secrets/$MINIO_SECRET_KEY_FILE"
if [ -f $ACCESS_KEY_FILE -a -f $SECRET_KEY_FILE ]; then if [ -f "$ACCESS_KEY_FILE" ] && [ -f "$SECRET_KEY_FILE" ]; then
if [ -f $ACCESS_KEY_FILE ]; then if [ -f "$ACCESS_KEY_FILE" ]; then
export MINIO_ACCESS_KEY="$(cat "$ACCESS_KEY_FILE")" MINIO_ACCESS_KEY="$(cat "$ACCESS_KEY_FILE")"
export MINIO_ACCESS_KEY
fi
if [ -f "$SECRET_KEY_FILE" ]; then
MINIO_SECRET_KEY="$(cat "$SECRET_KEY_FILE")"
export MINIO_SECRET_KEY
fi fi
if [ -f $SECRET_KEY_FILE ]; then fi
export MINIO_SECRET_KEY="$(cat "$SECRET_KEY_FILE")" }
## Create UID/GID based on available environment variables.
docker_set_uid_gid() {
addgroup -S "$MINIO_GROUPNAME" >/dev/null 2>&1 && \
adduser -S -G "$MINIO_GROUPNAME" "$MINIO_USERNAME" >/dev/null 2>&1
}
# su-exec to requested user, if user cannot be requested
# existing user is used automatically.
docker_switch_user() {
owner=$(check-user "$@")
if [ "${owner}" != "${MINIO_USERNAME}:${MINIO_GROUPNAME}" ]; then
## Print the message only if we are not using non-default username:groupname.
if [ "${MINIO_USERNAME}:${MINIO_GROUPNAME}" != "minio:minio" ]; then
echo "Requested username/group ${MINIO_USERNAME}:${MINIO_GROUPNAME} cannot be used"
echo "Found existing data with user ${owner}, we will continue and use ${owner} instead."
return
fi fi
fi fi
exec su-exec "${owner}" "$@"
} }
## Set access env from secrets if necessary. ## Set access env from secrets if necessary.
docker_secrets_env docker_secrets_env
exec "$@" ## User Input UID and GID
docker_set_uid_gid
## Switch to user if applicable.
docker_switch_user "$@"

@ -1,3 +1,5 @@
// +build ignore
/* /*
* MinIO Cloud Storage, (C) 2019 MinIO, Inc. * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
* *
@ -36,7 +38,7 @@ const (
initGraceTime = 300 initGraceTime = 300
healthPath = "/minio/health/live" healthPath = "/minio/health/live"
timeout = time.Duration(30 * time.Second) timeout = time.Duration(30 * time.Second)
minioProcess = "minio" tcp = "tcp"
) )
// returns container boot time by finding // returns container boot time by finding
@ -66,9 +68,16 @@ func findEndpoint() (string, error) {
// split netstat output in rows // split netstat output in rows
scanner := bufio.NewScanner(stdout) scanner := bufio.NewScanner(stdout)
scanner.Split(bufio.ScanLines) scanner.Split(bufio.ScanLines)
// loop over the rows to find MinIO process // MinIO works on TCP and it is supposed to be
// the only process listening on a port inside
// container. So we take the first row of netstat
// output (that has tcp) and assume that is the
// MinIO server port.
// Since MinIO is running as non-root user, we can
// no longer depend on the PID/Program name column
// of netstat output
for scanner.Scan() { for scanner.Scan() {
if strings.Contains(scanner.Text(), minioProcess) { if strings.Contains(scanner.Text(), tcp) {
line := scanner.Text() line := scanner.Text()
newLine := strings.Replace(line, ":::", "127.0.0.1:", 1) newLine := strings.Replace(line, ":::", "127.0.0.1:", 1)
fields := strings.Fields(newLine) fields := strings.Fields(newLine)

@ -58,6 +58,35 @@ docker run -p 9000:9000 --name minio1 \
minio/minio server /data minio/minio server /data
``` ```
### Run MinIO Docker as non root user
MinIO server runs as non-root within the container by default. However, this is applicable only if you're deploying new MinIO instance (not upgrading from older releases). Deployments upgrading from older MinIO deployments, will continue to run as the user previously used if any.
By default `minio` is username and groupname. Use environment variables `MINIO_USERNAME` and `MINIO_GROUPNAME` to override these default values.
#### GNU/Linux and macOS
```sh
docker run -p 9000:9000 --name minio1 \
-e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
-e "MINIO_USERNAME=custom" \
-e "MINIO_GROUPNAME=custom" \
-v /mnt/data:/data \
-v /mnt/config:/root/.minio \
minio/minio server /data
```
#### Windows
```powershell
docker run -p 9000:9000 --name minio1 \
-e "MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
-e "MINIO_USERNAME=custom" \
-e "MINIO_GROUPNAME=custom" \
-v D:\data:/data \
-v D:\minio\config:/root/.minio \
minio/minio server /data
```
### MinIO Custom Access and Secret Keys using Docker secrets ### MinIO Custom Access and Secret Keys using Docker secrets
To override MinIO's auto-generated keys, you may pass secret and access keys explicitly by creating access and secret keys as [Docker secrets](https://docs.docker.com/engine/swarm/secrets/). MinIO server also allows regular strings as access and secret keys. To override MinIO's auto-generated keys, you may pass secret and access keys explicitly by creating access and secret keys as [Docker secrets](https://docs.docker.com/engine/swarm/secrets/). MinIO server also allows regular strings as access and secret keys.

Loading…
Cancel
Save