refactor: run crontab under sudo to support rhel systems
RHEL blocks users from executing crontab as themselves Because Of Reasons so we need to use sudo instead. Also refactors file writing to properly support writing the permissions on sudoers files going forward. Closes #5020
This commit is contained in:
@@ -1 +1 @@
|
||||
dokku ALL=NOPASSWD:SETENV:/usr/bin/docker,/usr/bin/docker-image-labeler,/usr/bin/pack
|
||||
dokku ALL=NOPASSWD:SETENV:/usr/bin/docker,/usr/bin/docker-image-labeler,/usr/bin/pack,/usr/bin/crontab
|
||||
|
||||
@@ -183,6 +183,10 @@ func SetPermissions(path string, fileMode os.FileMode) error {
|
||||
|
||||
systemGroup := GetenvWithDefault("DOKKU_SYSTEM_GROUP", "dokku")
|
||||
systemUser := GetenvWithDefault("DOKKU_SYSTEM_USER", "dokku")
|
||||
if strings.HasPrefix("/etc/sudoers.d/", path) {
|
||||
systemGroup = "root"
|
||||
systemUser = "root"
|
||||
}
|
||||
|
||||
group, err := user.LookupGroup(systemGroup)
|
||||
if err != nil {
|
||||
@@ -207,7 +211,20 @@ func SetPermissions(path string, fileMode os.FileMode) error {
|
||||
|
||||
// WriteSliceToFile writes a slice of strings to a file
|
||||
func WriteSliceToFile(filename string, lines []string) error {
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
mode := os.FileMode(0600)
|
||||
if strings.HasPrefix("/etc/sudoers.d/", filename) {
|
||||
// sudoers files should be either 0600 (rhel) or 0440 (debian)
|
||||
defaultMode := map[string]bool{
|
||||
"centos": true,
|
||||
"fedora": true,
|
||||
"rhel": true,
|
||||
}
|
||||
if !defaultMode[os.Getenv("DOKKU_DISTRO")] {
|
||||
mode = os.FileMode(0440)
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -220,8 +237,8 @@ func WriteSliceToFile(filename string, lines []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
file.Chmod(0600)
|
||||
SetPermissions(filename, 0600)
|
||||
file.Chmod(mode)
|
||||
SetPermissions(filename, mode)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
1
plugins/cron/.gitignore
vendored
1
plugins/cron/.gitignore
vendored
@@ -3,5 +3,6 @@
|
||||
/triggers/*
|
||||
/triggers
|
||||
/cron-*
|
||||
/install
|
||||
/post-*
|
||||
/report
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SUBCOMMANDS = subcommands/list subcommands/report
|
||||
TRIGGERS = triggers/cron-write triggers/post-delete triggers/post-deploy triggers/report
|
||||
TRIGGERS = triggers/cron-write triggers/install triggers/post-delete triggers/post-deploy triggers/report
|
||||
BUILD = commands subcommands triggers
|
||||
PLUGIN_NAME = cron
|
||||
|
||||
|
||||
@@ -81,13 +81,13 @@ func fetchCronEntries(appName string) ([]templateCommand, error) {
|
||||
}
|
||||
|
||||
func deleteCrontab() error {
|
||||
command := common.NewShellCmd("crontab -l -u dokku")
|
||||
command := common.NewShellCmd("sudo /usr/bin/crontab -l -u dokku")
|
||||
command.ShowOutput = false
|
||||
if !command.Execute() {
|
||||
return nil
|
||||
}
|
||||
|
||||
command = common.NewShellCmd("crontab -r -u dokku")
|
||||
command = common.NewShellCmd("sudo /usr/bin/crontab -r -u dokku")
|
||||
command.ShowOutput = false
|
||||
out, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -163,7 +163,7 @@ func writeCronEntries() error {
|
||||
return fmt.Errorf("Unable to template out schedule file: %v", err)
|
||||
}
|
||||
|
||||
command := common.NewShellCmd(fmt.Sprintf("crontab -u dokku %s", tmpFile.Name()))
|
||||
command := common.NewShellCmd(fmt.Sprintf("sudo /usr/bin/crontab -u dokku %s", tmpFile.Name()))
|
||||
command.ShowOutput = false
|
||||
out, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
|
||||
@@ -20,6 +20,8 @@ func main() {
|
||||
switch trigger {
|
||||
case "cron-write":
|
||||
err = cron.TriggerCronWrite()
|
||||
case "install":
|
||||
err = cron.TriggerInstall()
|
||||
case "post-delete":
|
||||
err = cron.TriggerPostDelete()
|
||||
case "post-deploy":
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
)
|
||||
|
||||
// TriggerInstall installs a sudoers file so we can execute crontab via sudo
|
||||
func TriggerInstall() error {
|
||||
lines := []string{"%dokku ALL=(ALL) NOPASSWD:/usr/bin/crontab"}
|
||||
notty := map[string]bool{
|
||||
"centos": true,
|
||||
"fedora": true,
|
||||
"rhel": true,
|
||||
}
|
||||
if notty[os.Getenv("DOKKU_DISTRO")] {
|
||||
lines = append(lines, "Defaults:dokku !requiretty")
|
||||
}
|
||||
|
||||
return common.WriteSliceToFile("/etc/sudoers.d/dokku-cron", lines)
|
||||
}
|
||||
|
||||
// TriggerPostDelete updates the cron entries for all apps
|
||||
func TriggerPostDelete() error {
|
||||
return writeCronEntries()
|
||||
|
||||
@@ -37,6 +37,13 @@ teardown() {
|
||||
assert_output "$help_output"
|
||||
}
|
||||
|
||||
@test "(cron) installed" {
|
||||
run /bin/bash -c "test -f /etc/sudoers.d/dokku-cron"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "(cron) invalid [missing-keys]" {
|
||||
run deploy_app python dokku@dokku.me:$TEST_APP template_cron_file_invalid
|
||||
echo "output: $output"
|
||||
|
||||
Reference in New Issue
Block a user