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:
Jose Diaz-Gonzalez
2022-02-27 19:17:04 -05:00
parent 2e1df2578c
commit 257baa1d46
8 changed files with 56 additions and 8 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -3,5 +3,6 @@
/triggers/*
/triggers
/cron-*
/install
/post-*
/report

View File

@@ -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

View File

@@ -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 {

View File

@@ -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":

View File

@@ -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()

View File

@@ -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"