diff --git a/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala b/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala index aeae79d69..96c17e6de 100644 --- a/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala +++ b/src/main/scala/com/typesafe/sbt/packager/docker/DockerPlugin.scala @@ -112,10 +112,10 @@ object DockerPlugin extends AutoPlugin { val strategy = dockerPermissionStrategy.value val dockerBaseDirectory = (defaultLinuxInstallLocation in Docker).value val user = (daemonUser in Docker).value + val uidOpt = (daemonUserUid in Docker).value val group = (daemonGroup in Docker).value + val gidOpt = (daemonGroupGid in Docker).value val base = dockerBaseImage.value - val uid = 1001 - val gid = 0 val generalCommands = makeFrom(base) +: makeMaintainer((maintainer in Docker).value).toSeq val stage0name = "stage0" @@ -133,7 +133,11 @@ object DockerPlugin extends AutoPlugin { } val stage1: Seq[CmdLike] = generalCommands ++ - Seq(makeUserAdd(user, uid, gid), makeWorkdir(dockerBaseDirectory)) ++ + (uidOpt match { + case Some(_) => Seq(makeUser("root"), makeUserAdd(user, uidOpt, gidOpt)) + case _ => Seq() + }) ++ + Seq(makeWorkdir(dockerBaseDirectory)) ++ (strategy match { case DockerPermissionStrategy.MultiStage => Seq(makeCopyFrom(dockerBaseDirectory, stage0name, user, group)) @@ -148,7 +152,11 @@ object DockerPlugin extends AutoPlugin { dockerEnvVars.value.map(makeEnvVar) ++ makeExposePorts(dockerExposedPorts.value, dockerExposedUdpPorts.value) ++ makeVolumes(dockerExposedVolumes.value, user, group) ++ - Seq(makeUser(uid), makeEntrypoint(dockerEntrypoint.value), makeCmd(dockerCmd.value)) + Seq(uidOpt match { + case Some(uid) => makeUser(uid) + case _ => makeUser(user) + }) ++ + Seq(makeEntrypoint(dockerEntrypoint.value), makeCmd(dockerCmd.value)) stage0 ++ stage1 } @@ -188,8 +196,12 @@ object DockerPlugin extends AutoPlugin { stage := (stage dependsOn dockerGenerateConfig).value, stagingDirectory := (target in Docker).value / "stage", target := target.value / "docker", - daemonUser := "daemon", + // pick a user name that's unlikely to exist in base images + daemonUser := "demiourgos728", + // when daemonUserUid is set, we will try to create this user and set numeric USER + daemonUserUid := Some("1001"), daemonGroup := "root", + daemonGroupGid := Some("0"), defaultLinuxInstallLocation := "/opt/docker", validatePackage := Validation .runAndThrow(validatePackageValidators.value, streams.value.log), @@ -316,25 +328,17 @@ object DockerPlugin extends AutoPlugin { /** * @param daemonUser - * @param userId - * @param groupId - * @return useradd to create the daemon user with the given userId and groupId + * @param uidOpt + * @param gidOpt + * @return useradd to create the daemon user with the given uidOpt and gidOpt */ - private final def makeUserAdd(daemonUser: String, userId: Int, groupId: Int): CmdLike = + private final def makeUserAdd(daemonUser: String, uidOpt: Option[String], gidOpt: Option[String]): CmdLike = Cmd( "RUN", - "id", - "-u", - daemonUser, - "||", - "useradd", - "--system", - "--create-home", - "--uid", - userId.toString, - "--gid", - groupId.toString, - daemonUser + (List("id", "-u", daemonUser, "2>", "/dev/null", "||", "useradd", "--system", "--create-home") ::: + (uidOpt.fold[List[String]](Nil)(List("--uid", _))) ::: + (gidOpt.fold[List[String]](Nil)(List("--gid", _))) ::: + List(daemonUser)): _* ) /** @@ -344,13 +348,6 @@ object DockerPlugin extends AutoPlugin { private final def makeUser(daemonUser: String): CmdLike = Cmd("USER", daemonUser) - /** - * @param userId userId of the daemon user - * @return USER docker command - */ - private final def makeUser(userId: Int): CmdLike = - Cmd("USER", userId.toString) - /** * @param entrypoint * @return ENTRYPOINT command diff --git a/src/sbt-test/docker/file-permission/build.sbt b/src/sbt-test/docker/file-permission/build.sbt index f1a7c6ba7..aba07e026 100644 --- a/src/sbt-test/docker/file-permission/build.sbt +++ b/src/sbt-test/docker/file-permission/build.sbt @@ -20,9 +20,10 @@ lazy val root = (project in file(".")) |RUN ["chmod", "-R", "u=rX,g=rX", "/opt/docker"] | |FROM fabric8/java-centos-openjdk8-jdk - |RUN id -u daemon || useradd --system --create-home --uid 1001 --gid 0 daemon + |USER root + |RUN id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728 |WORKDIR /opt/docker - |COPY --from=stage0 --chown=daemon:root /opt/docker /opt/docker + |COPY --from=stage0 --chown=demiourgos728:root /opt/docker /opt/docker |USER 1001 |ENTRYPOINT ["/opt/docker/bin/file-permission-test"] |CMD []""".stripMargin.linesIterator.toList) @@ -33,7 +34,8 @@ lazy val root = (project in file(".")) val lines = dockerfile.linesIterator.toList assertEquals(lines, """FROM fabric8/java-centos-openjdk8-jdk - |RUN id -u daemon || useradd --system --create-home --uid 1001 --gid 0 daemon + |USER root + |RUN id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728 |WORKDIR /opt/docker |COPY opt /opt |USER 1001 @@ -46,7 +48,8 @@ lazy val root = (project in file(".")) val lines = dockerfile.linesIterator.toList assertEquals(lines, """FROM openjdk:8 - |RUN id -u daemon || useradd --system --create-home --uid 1001 --gid 0 daemon + |USER root + |RUN id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728 |WORKDIR /opt/docker |COPY opt /opt |RUN ["chmod", "-R", "u=rX,g=rX", "/opt/docker"] @@ -60,10 +63,9 @@ lazy val root = (project in file(".")) val lines = dockerfile.linesIterator.toList assertEquals(lines, """FROM fabric8/java-centos-openjdk8-jdk - |RUN id -u daemon || useradd --system --create-home --uid 1001 --gid 0 daemon |WORKDIR /opt/docker |COPY --chown=daemon:root opt /opt - |USER 1001 + |USER daemon |ENTRYPOINT ["/opt/docker/bin/file-permission-test"] |CMD []""".stripMargin.linesIterator.toList) }, @@ -79,9 +81,10 @@ lazy val root = (project in file(".")) |RUN ["chmod", "-R", "u=rwX,g=rwX", "/opt/docker"] | |FROM fabric8/java-centos-openjdk8-jdk - |RUN id -u daemon || useradd --system --create-home --uid 1001 --gid 0 daemon + |USER root + |RUN id -u demiourgos728 2> /dev/null || useradd --system --create-home --uid 1001 --gid 0 demiourgos728 |WORKDIR /opt/docker - |COPY --from=stage0 --chown=daemon:root /opt/docker /opt/docker + |COPY --from=stage0 --chown=demiourgos728:root /opt/docker /opt/docker |USER 1001 |ENTRYPOINT ["/opt/docker/bin/file-permission-test"] |CMD []""".stripMargin.linesIterator.toList) diff --git a/src/sbt-test/docker/file-permission/changes/strategy-copychown.sbt b/src/sbt-test/docker/file-permission/changes/strategy-copychown.sbt index b8a98e64f..e2d9592c0 100644 --- a/src/sbt-test/docker/file-permission/changes/strategy-copychown.sbt +++ b/src/sbt-test/docker/file-permission/changes/strategy-copychown.sbt @@ -2,3 +2,7 @@ import com.typesafe.sbt.packager.docker._ dockerPermissionStrategy := DockerPermissionStrategy.CopyChown dockerBaseImage := "fabric8/java-centos-openjdk8-jdk" + +// opt-out of numeric USER +daemonUserUid in Docker := None +daemonUser in Docker := "daemon" diff --git a/src/sphinx/formats/docker.rst b/src/sphinx/formats/docker.rst index a23dbe33b..0b8b97fbc 100644 --- a/src/sphinx/formats/docker.rst +++ b/src/sphinx/formats/docker.rst @@ -246,6 +246,17 @@ The files from ``mappings in Docker`` are extracted underneath this directory. defaultLinuxInstallLocation in Docker := "/opt/docker" +Daemon User +~~~~~~~~~~~ +By default, sbt Native Packager will create a daemon user named ``demiourgos728`` +whose UID is set to ``1001``, and and emit ``USER 1001`` since running as non-root is considered the best practice. + +The following can be used to emit ``USER daemon`` instead: + +.. code-block:: scala + + daemonUserUid in Docker := None + daemonUser in Docker := "daemon" File Permission ~~~~~~~~~~~~~~~