From c67475cbaaf951567924ee795743f4fb4c1e296d Mon Sep 17 00:00:00 2001 From: Josh Curl Date: Fri, 12 Aug 2016 11:05:16 -0700 Subject: [PATCH] Rewrite console.sh and docker-init in Go --- cmd/cloudinitexecute/cloudinitexecute.go | 7 +- cmd/console/console.go | 233 +++++++++++++++++++++++ cmd/dockerinit/main.go | 95 +++++++++ config/config.go | 6 + images/02-console/Dockerfile | 2 +- images/02-console/console.sh | 138 -------------- images/02-console/docker-init | 29 --- main.go | 5 + os-config.tpl.yml | 6 + util/util.go | 10 + 10 files changed, 359 insertions(+), 172 deletions(-) create mode 100644 cmd/console/console.go create mode 100644 cmd/dockerinit/main.go delete mode 100755 images/02-console/console.sh delete mode 100755 images/02-console/docker-init diff --git a/cmd/cloudinitexecute/cloudinitexecute.go b/cmd/cloudinitexecute/cloudinitexecute.go index 05e889c4..ff2395f8 100644 --- a/cmd/cloudinitexecute/cloudinitexecute.go +++ b/cmd/cloudinitexecute/cloudinitexecute.go @@ -9,10 +9,9 @@ import ( "path" "strings" - "github.com/docker/docker/pkg/mount" - log "github.com/Sirupsen/logrus" "github.com/coreos/coreos-cloudinit/system" + "github.com/docker/docker/pkg/mount" "github.com/rancher/os/config" "github.com/rancher/os/util" ) @@ -47,14 +46,14 @@ func Main() { } if console { - applyConsole(cfg) + ApplyConsole(cfg) } if preConsole { applyPreConsole(cfg) } } -func applyConsole(cfg *config.CloudConfig) { +func ApplyConsole(cfg *config.CloudConfig) { if len(cfg.SSHAuthorizedKeys) > 0 { authorizeSSHKeys("rancher", cfg.SSHAuthorizedKeys, sshKeyName) authorizeSSHKeys("docker", cfg.SSHAuthorizedKeys, sshKeyName) diff --git a/cmd/console/console.go b/cmd/console/console.go new file mode 100644 index 00000000..cf6d4f49 --- /dev/null +++ b/cmd/console/console.go @@ -0,0 +1,233 @@ +package console + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "regexp" + "strings" + "syscall" + + log "github.com/Sirupsen/logrus" + "github.com/rancher/os/cmd/cloudinitexecute" + "github.com/rancher/os/config" + "github.com/rancher/os/util" +) + +const ( + consoleDone = "/run/console-done" + dockerHome = "/home/docker" + rancherHome = "/home/rancher" + startScript = "/opt/rancher/bin/start.sh" +) + +func Main() { + cfg := config.LoadConfig() + + if err := os.MkdirAll(rancherHome, 2755); err != nil { + log.Error(err) + } + if err := os.Chown(rancherHome, 1100, 1100); err != nil { + log.Error(err) + } + + if err := os.MkdirAll(dockerHome, 2755); err != nil { + log.Error(err) + } + if err := os.Chown(dockerHome, 1101, 1101); err != nil { + log.Error(err) + } + + password := config.GetCmdline("rancher.password") + cmd := exec.Command("chpasswd") + cmd.Stdin = strings.NewReader(fmt.Sprint("rancher:", password)) + if err := cmd.Run(); err != nil { + log.Error(err) + } + + cmd = exec.Command("bash", "-c", `sed -E -i 's/(rancher:.*:).*(:.*:.*:.*:.*:.*:.*)$/\1\2/' /etc/shadow`) + if err := cmd.Run(); err != nil { + log.Error(err) + } + + if err := setupSSH(cfg); err != nil { + log.Error(err) + } + + respawn := ` +/sbin/getty 115200 tty6 +/sbin/getty 115200 tty5 +/sbin/getty 115200 tty4 +/sbin/getty 115200 tty3 +/sbin/getty 115200 tty2 +/sbin/getty 115200 tty1 +/usr/sbin/sshd -D` + + cmdline, err := ioutil.ReadFile("/proc/cmdline") + if err != nil { + log.Error(err) + } + cmdlineString := string(cmdline) + + for _, tty := range []string{"ttyS0", "ttyS1", "ttyS2", "ttyS3", "ttyAMA0"} { + if strings.Contains(cmdlineString, "console="+tty) { + respawn += "\n/sbin/getty 115200 " + tty + } + } + + if err = ioutil.WriteFile("/etc/respawn.conf", []byte(respawn), 0644); err != nil { + log.Error(err) + } + + if err = modifySshdConfig(); err != nil { + log.Error(err) + } + + if err = writeOsRelease(); err != nil { + log.Error(err) + } + + cmd = exec.Command("bash", "-c", `echo 'RancherOS \n \l' > /etc/issue`) + if err = cmd.Run(); err != nil { + log.Error(err) + } + + cmd = exec.Command("bash", "-c", `echo $(/sbin/ifconfig | grep -B1 "inet addr" |awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' |awk -F: '{ print $1 ": " $3}') >> /etc/issue`) + if err = cmd.Run(); err != nil { + log.Error(err) + } + + cloudinitexecute.ApplyConsole(cfg) + + if util.ExistsAndExecutable(config.CloudConfigScriptFile) { + cmd := exec.Command(config.CloudConfigScriptFile) + if err = cmd.Run(); err != nil { + log.Error(err) + } + } + + if util.ExistsAndExecutable(startScript) { + cmd := exec.Command(startScript) + if err = cmd.Run(); err != nil { + log.Error(err) + } + } + + if util.ExistsAndExecutable("/etc/rc.local") { + cmd := exec.Command("/etc/rc.local") + if err = cmd.Run(); err != nil { + log.Error(err) + } + } + + os.Setenv("TERM", "linux") + + respawnBinPath, err := exec.LookPath("respawn") + if err != nil { + log.Fatal(err) + } + + if err := ioutil.WriteFile(consoleDone, []byte(cfg.Rancher.Console), 0644); err != nil { + log.Error(err) + } + + log.Fatal(syscall.Exec(respawnBinPath, []string{"respawn", "-f", "/etc/respawn.conf"}, os.Environ())) +} + +func modifySshdConfig() error { + sshdConfig, err := ioutil.ReadFile("/etc/ssh/sshd_config") + if err != nil { + return err + } + sshdConfigString := string(sshdConfig) + + for _, item := range []string{ + "UseDNS no", + "PermitRootLogin no", + "ServerKeyBits 2048", + "AllowGroups docker", + } { + match, err := regexp.Match("^"+item, sshdConfig) + if err != nil { + return err + } + if !match { + sshdConfigString += fmt.Sprintf("%s\n", item) + } + } + + return ioutil.WriteFile("/etc/ssh/ssh_config", []byte(sshdConfigString), 0644) +} + +func writeOsRelease() error { + idLike := "busybox" + if osRelease, err := ioutil.ReadFile("/etc/os-release"); err == nil { + for _, line := range strings.Split(string(osRelease), "\n") { + if strings.HasPrefix(line, "ID_LIKE") { + split := strings.Split(line, "ID_LIKE") + if len(split) > 1 { + idLike = split[1] + } + } + } + } + + return ioutil.WriteFile("/etc/os-release", []byte(fmt.Sprintf(` +NAME="RancherOS" +VERSION=%s +ID=rancheros +ID_LIKE=%s +VERSION_ID=%s +PRETTY_NAME="RancherOS %s" +HOME_URL= +SUPPORT_URL= +BUG_REPORT_URL= +BUILD_ID= +`, config.VERSION, idLike, config.VERSION, config.VERSION)), 0644) +} + +func setupSSH(cfg *config.CloudConfig) error { + for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} { + outputFile := fmt.Sprintf("/etc/ssh/ssh_host_%s_key", keyType) + outputFilePub := fmt.Sprintf("/etc/ssh/ssh_host_%s_key.pub", keyType) + + if _, err := os.Stat(outputFile); err == nil { + continue + } + + saved, savedExists := cfg.Rancher.Ssh.Keys[keyType] + pub, pubExists := cfg.Rancher.Ssh.Keys[keyType+"-pub"] + + if savedExists && pubExists { + // TODO check permissions + if err := util.WriteFileAtomic(outputFile, []byte(saved), 0600); err != nil { + return err + } + if err := util.WriteFileAtomic(outputFilePub, []byte(pub), 0600); err != nil { + return err + } + continue + } + + cmd := exec.Command("bash", "-c", fmt.Sprintf("ssh-keygen -f %s -N '' -t %s", outputFile, keyType)) + if err := cmd.Run(); err != nil { + return err + } + + savedBytes, err := ioutil.ReadFile(outputFile) + if err != nil { + return err + } + + pubBytes, err := ioutil.ReadFile(outputFilePub) + if err != nil { + return err + } + + config.Set(fmt.Sprintf("rancher.ssh.keys.%s", keyType), string(savedBytes)) + config.Set(fmt.Sprintf("rancher.ssh.keys.%s-pub", keyType), string(pubBytes)) + } + + return os.MkdirAll("/var/run/sshd", 0644) +} diff --git a/cmd/dockerinit/main.go b/cmd/dockerinit/main.go new file mode 100644 index 00000000..dec48017 --- /dev/null +++ b/cmd/dockerinit/main.go @@ -0,0 +1,95 @@ +package dockerinit + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "strings" + "syscall" + "time" + + log "github.com/Sirupsen/logrus" + "github.com/rancher/os/util" +) + +const ( + consoleDone = "/run/console-done" + dockerConf = "/var/lib/rancher/conf/docker" + dockerLog = "/var/log/docker.log" +) + +func Main() { + if os.Getenv("DOCKER_CONF_SOURCED") == "" { + if err := sourceDockerConf(os.Args); err != nil { + log.Warnf("Failed to source %s: %v", dockerConf, err) + } + } + + for { + if _, err := os.Stat(consoleDone); err == nil { + break + } + time.Sleep(200 * time.Millisecond) + } + + dockerBin := "/usr/bin/docker" + for _, binPath := range []string{ + "/opt/bin", + "/usr/local/bin", + "/var/lib/rancher/docker", + } { + if util.ExistsAndExecutable(path.Join(binPath, "dockerd")) { + dockerBin = path.Join(binPath, "dockerd") + break + } + if util.ExistsAndExecutable(path.Join(binPath, "docker")) { + dockerBin = path.Join(binPath, "docker") + break + } + } + + if err := syscall.Mount("", "/", "", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil { + log.Error(err) + } + if err := syscall.Mount("", "/run", "", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil { + log.Error(err) + } + + mountInfo, err := ioutil.ReadFile("/proc/self/mountinfo") + if err != nil { + log.Fatal(err) + } + + for _, mount := range strings.Split(string(mountInfo), "\n") { + if strings.Contains(mount, "/var/lib/docker /var/lib/docker") && strings.Contains(mount, "rootfs") { + os.Setenv("DOCKER_RAMDISK", "1") + } + } + + args := []string{ + "dockerlaunch", + dockerBin, + } + + if len(os.Args) > 1 { + args = append(args, os.Args[1:]...) + } + + if os.Getenv("DOCKER_OPTS") != "" { + args = append(args, os.Getenv("DOCKER_OPTS")) + } + + log.Fatal(syscall.Exec("/usr/bin/dockerlaunch", args, os.Environ())) +} + +func sourceDockerConf(args []string) error { + args = append([]string{ + "bash", + "-c", + fmt.Sprintf(`[ -e %s ] && source %s; exec docker-init "$@" >> %s 2>&1`, dockerConf, dockerConf, dockerLog), + }, args...) + env := os.Environ() + env = append(env, "DOCKER_CONF_SOURCED=1") + return syscall.Exec("/bin/bash", args, env) +} diff --git a/config/config.go b/config/config.go index f22646bd..108a24a4 100644 --- a/config/config.go +++ b/config/config.go @@ -39,6 +39,12 @@ func Get(key string) (interface{}, error) { return v, nil } +func GetCmdline(key string) interface{} { + cmdline := readCmdline() + v, _ := getOrSetVal(key, cmdline, nil) + return v +} + func Set(key string, value interface{}) error { existing, err := readConfigs(nil, false, true, CloudConfigFile) if err != nil { diff --git a/images/02-console/Dockerfile b/images/02-console/Dockerfile index c63e7c9f..9f6e041c 100644 --- a/images/02-console/Dockerfile +++ b/images/02-console/Dockerfile @@ -1,5 +1,5 @@ FROM rancher/os-base -COPY console.sh docker-init update-ssh-keys rancheros-install /usr/sbin/ +COPY update-ssh-keys rancheros-install /usr/sbin/ COPY build/lsb-release /etc/ RUN sed -i 's/rancher:!/rancher:*/g' /etc/shadow && \ sed -i 's/docker:!/docker:*/g' /etc/shadow && \ diff --git a/images/02-console/console.sh b/images/02-console/console.sh deleted file mode 100755 index c558d777..00000000 --- a/images/02-console/console.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash -set -e -x - -setup_ssh() -{ - for i in rsa dsa ecdsa ed25519; do - local output=/etc/ssh/ssh_host_${i}_key - if [ ! -s $output ]; then - local saved="$(ros config get rancher.ssh.keys.${i})" - local pub="$(ros config get rancher.ssh.keys.${i}-pub)" - - if [[ -n "$saved" && -n "$pub" ]]; then - ( - umask 077 - temp_file=$(mktemp) - echo "$saved" > ${temp_file} - mv ${temp_file} ${output} - temp_file=$(mktemp) - echo "$pub" > ${temp_file} - mv ${temp_file} ${output}.pub - ) - else - ssh-keygen -f $output -N '' -t $i - ros config set -- rancher.ssh.keys.${i} "$(<${output})" - ros config set -- rancher.ssh.keys.${i}-pub "$(<${output}.pub)" - fi - fi - done - - mkdir -p /var/run/sshd -} - -RANCHER_HOME=/home/rancher -if [ ! -d ${RANCHER_HOME} ]; then - mkdir -p ${RANCHER_HOME} - chown rancher:rancher ${RANCHER_HOME} - chmod 2755 ${RANCHER_HOME} -fi - -DOCKER_HOME=/home/docker -if [ ! -d ${DOCKER_HOME} ]; then - mkdir -p ${DOCKER_HOME} - chown docker:docker ${DOCKER_HOME} - chmod 2755 ${DOCKER_HOME} -fi - -echo 1000000000 > /proc/sys/fs/file-max - -for i in $( /etc/respawn.conf << EOF -/sbin/getty 115200 tty6 -/sbin/getty 115200 tty5 -/sbin/getty 115200 tty4 -/sbin/getty 115200 tty3 -/sbin/getty 115200 tty2 -/sbin/getty 115200 tty1 -/usr/sbin/sshd -D -EOF - -for i in ttyS{0..4} ttyAMA0; do - if grep -q 'console='$i /proc/cmdline; then - echo '/sbin/getty 115200' $i >> /etc/respawn.conf - fi -done - -if ! grep -q '^UseDNS no' /etc/ssh/sshd_config; then - echo "UseDNS no" >> /etc/ssh/sshd_config -fi - -if ! grep -q '^PermitRootLogin no' /etc/ssh/sshd_config; then - echo "PermitRootLogin no" >> /etc/ssh/sshd_config -fi - -if ! grep -q '^ServerKeyBits 2048' /etc/ssh/sshd_config; then - echo "ServerKeyBits 2048" >> /etc/ssh/sshd_config -fi - -if ! grep -q '^AllowGroups docker' /etc/ssh/sshd_config; then - echo "AllowGroups docker" >> /etc/ssh/sshd_config -fi - -VERSION="$(ros os version)" -ID_TYPE="busybox" -if [ -e /etc/os-release ] && grep -q 'ID_LIKE=' /etc/os-release; then - ID_TYPE=$(grep 'ID_LIKE=' /etc/os-release | cut -d'=' -f2) -fi - -cat > /etc/os-release << EOF -NAME="RancherOS" -VERSION=$VERSION -ID=rancheros -ID_LIKE=$ID_TYPE -VERSION_ID=$VERSION -PRETTY_NAME="RancherOS $VERSION" -HOME_URL= -SUPPORT_URL= -BUG_REPORT_URL= -BUILD_ID= -EOF - -echo 'RancherOS \n \l' > /etc/issue -echo $(/sbin/ifconfig | grep -B1 "inet addr" |awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' |awk -F: '{ print $1 ": " $3}') >> /etc/issue - -cloud-init-execute -console - -if [ -x /var/lib/rancher/conf/cloud-config-script ]; then - echo "Running /var/lib/rancher/conf/cloud-config-script" - /var/lib/rancher/conf/cloud-config-script || true -fi - -if [ -x /opt/rancher/bin/start.sh ]; then - echo Executing custom script - /opt/rancher/bin/start.sh || true -fi - -echo `ros config get rancher.console` > /run/console-done - -if [ -x /etc/rc.local ]; then - echo Executing rc.local - /etc/rc.local || true -fi - -export TERM=linux -exec respawn -f /etc/respawn.conf diff --git a/images/02-console/docker-init b/images/02-console/docker-init deleted file mode 100755 index 6bfbd6fd..00000000 --- a/images/02-console/docker-init +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -e - -if [ -e /var/lib/rancher/conf/docker ]; then - source /var/lib/rancher/conf/docker -fi - -while [ ! -e /run/console-done ]; do - sleep 1 -done - -DOCKER_BIN=$(which docker) || DOCKER_BIN=/usr/bin/docker - -for i in /opt/bin /usr/local/bin; do - if [ -x ${i}/docker ]; then - PATH=${i}:$PATH - DOCKER_BIN=${i}/docker - break - fi -done - -mount --make-shared / -mount --make-shared /run - -if [ "$(grep '/var/lib/docker /var/lib/docker ' /proc/self/mountinfo | awk '{print $9}')" = "rootfs" ]; then - export DOCKER_RAMDISK=1 -fi - -exec /usr/bin/dockerlaunch $DOCKER_BIN "$@" $DOCKER_OPTS >>/var/log/docker.log 2>&1 diff --git a/main.go b/main.go index 4329b559..c225e282 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,9 @@ import ( "github.com/rancher/docker-from-scratch" "github.com/rancher/os/cmd/cloudinitexecute" "github.com/rancher/os/cmd/cloudinitsave" + "github.com/rancher/os/cmd/console" "github.com/rancher/os/cmd/control" + "github.com/rancher/os/cmd/dockerinit" "github.com/rancher/os/cmd/network" "github.com/rancher/os/cmd/power" "github.com/rancher/os/cmd/respawn" @@ -24,7 +26,10 @@ import ( var entrypoints = map[string]func(){ "cloud-init-execute": cloudinitexecute.Main, "cloud-init-save": cloudinitsave.Main, + "console": console.Main, + "console.sh": console.Main, "docker": docker.Main, + "docker-init": dockerinit.Main, "dockerlaunch": dockerlaunch.Main, "halt": power.Halt, "init": osInit.MainInit, diff --git a/os-config.tpl.yml b/os-config.tpl.yml index 4a2faeb0..64fab571 100644 --- a/os-config.tpl.yml +++ b/os-config.tpl.yml @@ -78,6 +78,8 @@ rancher: fstype: auto oem_fstype: auto oem_dev: LABEL=RANCHER_OEM + sysctl: + fs.file-max: 1000000000 services: {{if eq "amd64" .ARCH -}} acpid: @@ -177,8 +179,12 @@ rancher: - /usr/bin/ros:/usr/sbin/netconf:ro - /usr/bin/ros:/usr/sbin/wait-for-docker:ro - /usr/bin/ros:/usr/bin/switch-console:ro + - /usr/bin/ros:/usr/bin/console:ro + - /usr/bin/ros:/usr/sbin/console.sh:ro + - /usr/bin/ros:/usr/sbin/docker-init:ro console: image: {{.OS_REPO}}/os-console:{{.VERSION}}{{.SUFFIX}} + command: console labels: io.rancher.os.scope: system io.rancher.os.after: network diff --git a/util/util.go b/util/util.go index 07ddcf97..90cddc88 100644 --- a/util/util.go +++ b/util/util.go @@ -231,3 +231,13 @@ func UnescapeKernelParams(s string) string { s = strings.Replace(s, `\'`, `'`, -1) return s } + +func ExistsAndExecutable(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + + mode := info.Mode().Perm() + return mode&os.ModePerm != 0 +}