🎉 Initial commit, all commands basically working
Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at>
This commit is contained in:
28
cmd/Readme.md
Normal file
28
cmd/Readme.md
Normal file
@ -0,0 +1,28 @@
|
||||
# apkovl cli
|
||||
|
||||
This is a little CLI Tool to ease the changes that I usually make to the Alpine Linux FS Overlay files.
|
||||
|
||||
My usual workflow is using Alpine on RaspberryPis and adding small binarys and services to them.
|
||||
This tool was created to make simple tasks much faster and without the need to do them on an actual device
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
- [x] Make apkovl adjustable config option
|
||||
- [x] make user for passwd an argument, default to root
|
||||
- [x] make service templates via -t (from embedded fs, add pintimer, add standard go service)
|
||||
- [x] Edit hostname ? (needs to change hostname / hosts / and file name I guess ?)
|
||||
- [x] make service enable work
|
||||
- [x] make seperate compress / decompress action
|
||||
- [x] Edit IP Config ?! optional...
|
||||
- [x] Insert ssh key
|
||||
- [x] make ssh config change possible
|
||||
|
||||
|
||||
|
||||
# Contribute
|
||||
|
||||
Liked this Tool? You can **support** me by sending me a :coffee:
|
||||
[Coffee](https://paypal.me/lukasbachschwell/5).
|
||||
|
||||
Otherwise I really welcome **Pull Requests**.
|
80
cmd/general.go
Normal file
80
cmd/general.go
Normal file
@ -0,0 +1,80 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"apkovl-cli/compression"
|
||||
"embed"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
log "github.com/s00500/env_logger"
|
||||
)
|
||||
|
||||
//go:embed templates/*
|
||||
var embeddedFS embed.FS
|
||||
|
||||
func prepareWorkdir() {
|
||||
log.Info("Extracting APKovl ", apkovlFile, " to: ", workdir)
|
||||
// Extract apkovl
|
||||
err := os.RemoveAll(workdir)
|
||||
log.MustFatal(err)
|
||||
err = os.MkdirAll(workdir, 0755)
|
||||
log.MustFatal(err)
|
||||
r, err := os.Open(apkovlFile)
|
||||
log.MustFatal(err)
|
||||
err = compression.ExtractTarGz(r, workdir)
|
||||
log.MustFatal(err)
|
||||
}
|
||||
|
||||
func createOutput() {
|
||||
log.Info("Compress Workdir (", workdir, "), outputting: ", apkovlFileOutput)
|
||||
//Compress APKovl
|
||||
err := compression.Compress(workdir, apkovlFileOutput)
|
||||
log.MustFatal(err)
|
||||
}
|
||||
|
||||
func copyEmbeddedFile(src, dst string) error {
|
||||
srcfd, err := embeddedFS.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer srcfd.Close()
|
||||
|
||||
dstfd, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstfd.Close()
|
||||
|
||||
if _, err = io.Copy(dstfd, srcfd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil // Do not change permissions
|
||||
}
|
||||
|
||||
// copyFile copies a single file from src to dst
|
||||
func copyFile(src, dst string) error {
|
||||
var err error
|
||||
var srcfd *os.File
|
||||
var dstfd *os.File
|
||||
var srcinfo os.FileInfo
|
||||
|
||||
if srcfd, err = os.Open(src); err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcfd.Close()
|
||||
|
||||
if dstfd, err = os.Create(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstfd.Close()
|
||||
|
||||
if _, err = io.Copy(dstfd, srcfd); err != nil {
|
||||
return err
|
||||
}
|
||||
if srcinfo, err = os.Stat(src); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chmod(dst, srcinfo.Mode())
|
||||
}
|
76
cmd/hostname.go
Normal file
76
cmd/hostname.go
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/s00500/env_logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// hostnameCmd represents the hostname command
|
||||
var hostnameCmd = &cobra.Command{
|
||||
Use: "hostname",
|
||||
Short: "Change the hostname of the system easily",
|
||||
Long: `Change the hostname of the system easily`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
hostname := args[0]
|
||||
log.Println("changing hostname to ", hostname)
|
||||
|
||||
prepareWorkdir()
|
||||
|
||||
// read current
|
||||
fileDat, err := os.ReadFile(filepath.Join(workdir, "etc", "hostname"))
|
||||
log.MustFatal(err)
|
||||
oldName := strings.TrimSpace(string(fileDat))
|
||||
if oldName == "" {
|
||||
log.Fatal("Could not get old hostname")
|
||||
}
|
||||
|
||||
// Write new Hostnbame
|
||||
err = os.WriteFile(filepath.Join(workdir, "etc", "hostname"), []byte(hostname), 0755)
|
||||
log.MustFatal(err)
|
||||
|
||||
// Finally replace all in host file
|
||||
hostsDat, err := os.ReadFile(filepath.Join(workdir, "etc", "hosts"))
|
||||
log.MustFatal(err)
|
||||
|
||||
hostsNew := strings.ReplaceAll(string(hostsDat), oldName, hostname)
|
||||
|
||||
err = os.WriteFile(filepath.Join(workdir, "etc", "hosts"), []byte(hostsNew), 0755)
|
||||
log.MustFatal(err)
|
||||
|
||||
createOutput()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(hostnameCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// hostnameCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// hostnameCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
39
cmd/ifconfig.go
Normal file
39
cmd/ifconfig.go
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
log "github.com/s00500/env_logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ifconfigCmd represents the ifconfig command
|
||||
var ifconfigCmd = &cobra.Command{
|
||||
Use: "ifconfig",
|
||||
Short: "Edit network interface config",
|
||||
Long: `Edit network interface config`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Println("ifconfig called")
|
||||
|
||||
prepareWorkdir()
|
||||
editFile(workdir + "/etc/network/interfaces")
|
||||
createOutput()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(ifconfigCmd)
|
||||
}
|
36
cmd/pack.go
Normal file
36
cmd/pack.go
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
log "github.com/s00500/env_logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// packCmd represents the pack command
|
||||
var packCmd = &cobra.Command{
|
||||
Use: "pack",
|
||||
Short: "Pack the workdir and create a new overlay file",
|
||||
Long: `Pack the workdir and create a new overlay file`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Println("pack called")
|
||||
createOutput()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(packCmd)
|
||||
}
|
106
cmd/passwd.go
Normal file
106
cmd/passwd.go
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GehirnInc/crypt"
|
||||
_ "github.com/GehirnInc/crypt/sha512_crypt"
|
||||
log "github.com/s00500/env_logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// passwdCmd represents the passwd command
|
||||
var passwdCmd = &cobra.Command{
|
||||
Use: "passwd",
|
||||
Short: "Change a user password by editing /etc/shadow",
|
||||
Long: `Change a user password by editing /etc/shadow, first argument is user (defaults to root), second is password, can also be enteres via promt if not specified`,
|
||||
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Println("passwd called")
|
||||
|
||||
// promt for a user
|
||||
user := "root"
|
||||
if len(args) > 0 {
|
||||
user = args[0]
|
||||
}
|
||||
|
||||
// promt for a password
|
||||
newPassword := ""
|
||||
if len(args) > 1 {
|
||||
newPassword = args[1]
|
||||
} else {
|
||||
newPassword = promtPassword("New Password")
|
||||
}
|
||||
|
||||
if newPassword == "" {
|
||||
log.Fatal("No password specified")
|
||||
}
|
||||
|
||||
prepareWorkdir()
|
||||
|
||||
// Modify /etc/shadow
|
||||
pwHash := encryptPassword(newPassword)
|
||||
shadowDat, err := os.ReadFile(workdir + "/etc/shadow")
|
||||
log.MustFatal(err)
|
||||
newShadow := ""
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(shadowDat)))
|
||||
for scanner.Scan() {
|
||||
if strings.HasPrefix(scanner.Text(), user+":") {
|
||||
parts := strings.Split(scanner.Text(), ":")
|
||||
//log.Info(scanner.Text())
|
||||
parts[1] = pwHash
|
||||
//log.Info(strings.Join(parts, ":") + "\n")
|
||||
newShadow += strings.Join(parts, ":") + "\n"
|
||||
continue
|
||||
}
|
||||
newShadow += scanner.Text() + "\n"
|
||||
}
|
||||
|
||||
err = os.WriteFile(workdir+"/etc/shadow", []byte(newShadow), 0600)
|
||||
log.MustFatal(err)
|
||||
|
||||
createOutput()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(passwdCmd)
|
||||
}
|
||||
|
||||
func encryptPassword(userPassword string) string {
|
||||
// Generate a random string for use in the salt
|
||||
const charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
s := make([]byte, 8)
|
||||
for i := range s {
|
||||
s[i] = charset[seededRand.Intn(len(charset))]
|
||||
}
|
||||
salt := []byte(fmt.Sprintf("$6$%s", s))
|
||||
// use salt to hash user-supplied password
|
||||
crypt := crypt.SHA512.New()
|
||||
hash, err := crypt.Generate([]byte(userPassword), salt)
|
||||
if err != nil {
|
||||
log.Fatalf("error hashing user's supplied password: %s\n", err)
|
||||
}
|
||||
return string(hash)
|
||||
}
|
49
cmd/promt-helpers.go
Normal file
49
cmd/promt-helpers.go
Normal file
@ -0,0 +1,49 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
log "github.com/s00500/env_logger"
|
||||
)
|
||||
|
||||
func promtString(name string) string {
|
||||
validate := func(input string) error {
|
||||
if input == "" {
|
||||
return errors.New("empty string")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
prompt := promptui.Prompt{
|
||||
Label: name,
|
||||
Validate: validate,
|
||||
}
|
||||
|
||||
result, err := prompt.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func promtPassword(name string) string {
|
||||
validate := func(input string) error {
|
||||
if input == "" {
|
||||
return errors.New("empty string")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
prompt := promptui.Prompt{
|
||||
Label: name,
|
||||
Validate: validate,
|
||||
HideEntered: true,
|
||||
}
|
||||
|
||||
result, err := prompt.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return result
|
||||
}
|
87
cmd/root.go
Normal file
87
cmd/root.go
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var cfgFile string
|
||||
var apkovlFile string
|
||||
var apkovlFileOutput string
|
||||
var workdir string = "apkovlworkdir"
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "apkovl-cli",
|
||||
Short: "CLI to modify and change alpine linux local backups (.apkovl)",
|
||||
Long: `CLI to modify and change alpine linux local backups (.apkovl)`,
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
// Run: func(cmd *cobra.Command, args []string) { },
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
// Here you will define your flags and configuration settings.
|
||||
// Cobra supports persistent flags, which, if defined here,
|
||||
// will be global for your application.
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.apkovl-cli.yaml)")
|
||||
rootCmd.PersistentFlags().StringVarP(&apkovlFile, "apkovlFile", "a", "localhost.apkovl.tar.gz", "apkovlFile file to work on")
|
||||
rootCmd.PersistentFlags().StringVarP(&apkovlFileOutput, "apkovlFileOutput", "o", "localhost_edited.apkovl.tar.gz", "apkovlFile file to output too, can be the same as input file")
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
func initConfig() {
|
||||
if cfgFile != "" {
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Find home directory.
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Search config in home directory with name ".apkovl-cli" (without extension).
|
||||
viper.AddConfigPath(home)
|
||||
viper.SetConfigName(".apkovl-cli")
|
||||
}
|
||||
|
||||
viper.AutomaticEnv() // read in environment variables that match
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
111
cmd/service.go
Normal file
111
cmd/service.go
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/s00500/env_logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var serviceTemplate string
|
||||
var serviceBootLevel string
|
||||
|
||||
// serviceCmd represents the service command
|
||||
var serviceCmd = &cobra.Command{
|
||||
Use: "service",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Println("service called")
|
||||
|
||||
if len(args) == 0 {
|
||||
log.Fatal("Please specify a service name")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
prepareWorkdir()
|
||||
// Make changes:
|
||||
// Edit the servicefile
|
||||
|
||||
if serviceBootLevel != "" {
|
||||
log.Info("Setting boot level to ", serviceBootLevel)
|
||||
if serviceBootLevel == "none" {
|
||||
dirs, err := os.ReadDir(workdir + "/etc/runlevels")
|
||||
log.MustFatal(err)
|
||||
for _, dir := range dirs {
|
||||
log.Info("Checking ", filepath.Join(dir.Name(), name))
|
||||
os.RemoveAll(filepath.Join(dir.Name(), name))
|
||||
}
|
||||
|
||||
createOutput()
|
||||
return
|
||||
}
|
||||
|
||||
err := os.Symlink("/etc/init.d/"+name, filepath.Join(workdir, "etc", "runlevels", serviceBootLevel, name))
|
||||
log.MustFatal(err)
|
||||
createOutput()
|
||||
return
|
||||
}
|
||||
|
||||
err := os.MkdirAll(workdir+"/etc/init.d", 0755)
|
||||
log.MustFatal(err)
|
||||
|
||||
if serviceTemplate != "" {
|
||||
// Copy over template file from embed fs to service location
|
||||
err = copyEmbeddedFile("templates/service/"+serviceTemplate, workdir+"/etc/init.d/"+name)
|
||||
log.MustFatal(err)
|
||||
}
|
||||
|
||||
editFile(workdir + "/etc/init.d/" + name)
|
||||
|
||||
createOutput()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serviceCmd)
|
||||
|
||||
serviceCmd.Flags().StringVarP(&serviceTemplate, "template", "t", "", "Create new service from a template")
|
||||
serviceCmd.Flags().StringVar(&serviceBootLevel, "boot", "", "Specify a boot level, use 'none' for disable")
|
||||
}
|
||||
|
||||
func editFile(path string) {
|
||||
editor := os.Getenv("EDITOR")
|
||||
if editor == "" {
|
||||
editor = "nano"
|
||||
log.Info("EDITOR: nano (default)")
|
||||
} else {
|
||||
log.Info("EDITOR: ", editor)
|
||||
}
|
||||
|
||||
editC := exec.Command(editor, path)
|
||||
log.Debug("Calling ", editC)
|
||||
editC.Stdout = os.Stdout
|
||||
editC.Stdin = os.Stdin
|
||||
editC.Stderr = os.Stderr
|
||||
|
||||
log.Should(editC.Start())
|
||||
log.Should(editC.Wait())
|
||||
}
|
61
cmd/ssh-config.go
Normal file
61
cmd/ssh-config.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
log "github.com/s00500/env_logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var sshPermitRoot bool
|
||||
|
||||
// configCmd represents the config command
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Change the sshd configuration file",
|
||||
Long: `Change the sshd configuration file`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Println("ssh config called")
|
||||
|
||||
prepareWorkdir()
|
||||
// Make changes:
|
||||
|
||||
if sshPermitRoot {
|
||||
// Copy over template file from embed fs to service location
|
||||
err := copyEmbeddedFile("templates/sshd/sshd_config_permit_root"+serviceTemplate, workdir+"/etc/ssh/sshd_config")
|
||||
log.MustFatal(err)
|
||||
}
|
||||
|
||||
editFile(workdir + "/etc/ssh/sshd_config")
|
||||
|
||||
createOutput()
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
sshCmd.AddCommand(configCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// configCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
configCmd.Flags().BoolVar(&sshPermitRoot, "permit-root", false, "Activate root login with password")
|
||||
}
|
84
cmd/ssh-key.go
Normal file
84
cmd/ssh-key.go
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/s00500/env_logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var keyFile string
|
||||
|
||||
// keyCmd represents the key command
|
||||
var keyCmd = &cobra.Command{
|
||||
Use: "key",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Info("key called")
|
||||
|
||||
// promt for a user
|
||||
userName := "root"
|
||||
if len(args) > 0 {
|
||||
userName = args[0]
|
||||
}
|
||||
|
||||
if strings.Contains(keyFile, "~") {
|
||||
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
keyFile = strings.Replace(keyFile, "~", usr.HomeDir, 1)
|
||||
}
|
||||
|
||||
keyDat, err := os.ReadFile(keyFile)
|
||||
log.MustFatal(err)
|
||||
|
||||
prepareWorkdir()
|
||||
|
||||
path := workdir + "/home/" + userName + "/.ssh"
|
||||
if userName == "root" {
|
||||
path = workdir + "/root/.ssh"
|
||||
}
|
||||
err = os.MkdirAll(path, 0755)
|
||||
log.MustFatal(err)
|
||||
|
||||
f, err := os.OpenFile(filepath.Join(path, "authorized_keys"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
log.MustFatal(err)
|
||||
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(string(keyDat) + "\n")
|
||||
log.MustFatal(err)
|
||||
|
||||
createOutput()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
sshCmd.AddCommand(keyCmd)
|
||||
keyCmd.Flags().StringVar(&keyFile, "t", "~/.ssh/id_rsa.pub", "Public Key File to insert into overlay")
|
||||
}
|
51
cmd/ssh.go
Normal file
51
cmd/ssh.go
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// sshCmd represents the ssh command
|
||||
var sshCmd = &cobra.Command{
|
||||
Use: "ssh",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("ssh called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(sshCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// sshCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// sshCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
11
cmd/templates/scripts/pintimer.sh
Normal file
11
cmd/templates/scripts/pintimer.sh
Normal file
@ -0,0 +1,11 @@
|
||||
#!/bin/ash
|
||||
|
||||
echo 22 > /sys/class/gpio/export
|
||||
echo out > /sys/class/gpio/gpio22/direction
|
||||
echo 1 > /sys/class/gpio/gpio22/value
|
||||
|
||||
#while :
|
||||
#do
|
||||
# echo "Press [CTRL+C] to stop.."
|
||||
# sleep 1
|
||||
#done
|
8
cmd/templates/service/basicService
Normal file
8
cmd/templates/service/basicService
Normal file
@ -0,0 +1,8 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name=$RC_SVCNAME
|
||||
description="<YOUR Service Description>"
|
||||
|
||||
pidfile="/run/$RC_SVCNAME.pid"
|
||||
command="/media/mmcblk0p1/<YOUR Binary Name>"
|
||||
command_background="yes"
|
4
cmd/templates/service/pintimer
Normal file
4
cmd/templates/service/pintimer
Normal file
@ -0,0 +1,4 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="pintimer"
|
||||
command="/etc/sbin/pintimer.sh"
|
117
cmd/templates/sshd/sshd_config
Normal file
117
cmd/templates/sshd/sshd_config
Normal file
@ -0,0 +1,117 @@
|
||||
# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $
|
||||
|
||||
# This is the sshd server system-wide configuration file. See
|
||||
# sshd_config(5) for more information.
|
||||
|
||||
# This sshd was compiled with PATH=/bin:/usr/bin:/sbin:/usr/sbin
|
||||
|
||||
# The strategy used for options in the default sshd_config shipped with
|
||||
# OpenSSH is to specify options with their default value where
|
||||
# possible, but leave them commented. Uncommented options override the
|
||||
# default value.
|
||||
|
||||
#Port 22
|
||||
#AddressFamily any
|
||||
#ListenAddress 0.0.0.0
|
||||
#ListenAddress ::
|
||||
|
||||
#HostKey /etc/ssh/ssh_host_rsa_key
|
||||
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
||||
#HostKey /etc/ssh/ssh_host_ed25519_key
|
||||
|
||||
# Ciphers and keying
|
||||
#RekeyLimit default none
|
||||
|
||||
# Logging
|
||||
#SyslogFacility AUTH
|
||||
#LogLevel INFO
|
||||
|
||||
# Authentication:
|
||||
|
||||
#LoginGraceTime 2m
|
||||
#PermitRootLogin without-password
|
||||
#StrictModes yes
|
||||
#MaxAuthTries 6
|
||||
#MaxSessions 10
|
||||
|
||||
#PubkeyAuthentication yes
|
||||
|
||||
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
|
||||
# but this is overridden so installations will only check .ssh/authorized_keys
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
|
||||
#AuthorizedPrincipalsFile none
|
||||
|
||||
#AuthorizedKeysCommand none
|
||||
#AuthorizedKeysCommandUser nobody
|
||||
|
||||
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
|
||||
#HostbasedAuthentication no
|
||||
# Change to yes if you don't trust ~/.ssh/known_hosts for
|
||||
# HostbasedAuthentication
|
||||
#IgnoreUserKnownHosts no
|
||||
# Don't read the user's ~/.rhosts and ~/.shosts files
|
||||
#IgnoreRhosts yes
|
||||
|
||||
# To disable tunneled clear text passwords, change to no here!
|
||||
#PasswordAuthentication yes
|
||||
#PermitEmptyPasswords no
|
||||
|
||||
# Change to no to disable s/key passwords
|
||||
#ChallengeResponseAuthentication yes
|
||||
|
||||
# Kerberos options
|
||||
#KerberosAuthentication no
|
||||
#KerberosOrLocalPasswd yes
|
||||
#KerberosTicketCleanup yes
|
||||
#KerberosGetAFSToken no
|
||||
|
||||
# GSSAPI options
|
||||
#GSSAPIAuthentication no
|
||||
#GSSAPICleanupCredentials yes
|
||||
|
||||
# Set this to 'yes' to enable PAM authentication, account processing,
|
||||
# and session processing. If this is enabled, PAM authentication will
|
||||
# be allowed through the ChallengeResponseAuthentication and
|
||||
# PasswordAuthentication. Depending on your PAM configuration,
|
||||
# PAM authentication via ChallengeResponseAuthentication may bypass
|
||||
# the setting of "PermitRootLogin without-password".
|
||||
# If you just want the PAM account and session checks to run without
|
||||
# PAM authentication, then enable this but set PasswordAuthentication
|
||||
# and ChallengeResponseAuthentication to 'no'.
|
||||
#UsePAM no
|
||||
|
||||
#AllowAgentForwarding yes
|
||||
# Feel free to re-enable these if your use case requires them.
|
||||
AllowTcpForwarding no
|
||||
GatewayPorts no
|
||||
X11Forwarding no
|
||||
#X11DisplayOffset 10
|
||||
#X11UseLocalhost yes
|
||||
#PermitTTY yes
|
||||
#PrintMotd yes
|
||||
#PrintLastLog yes
|
||||
#TCPKeepAlive yes
|
||||
#PermitUserEnvironment no
|
||||
#Compression delayed
|
||||
#ClientAliveInterval 0
|
||||
#ClientAliveCountMax 3
|
||||
#UseDNS no
|
||||
#PidFile /run/sshd.pid
|
||||
#MaxStartups 10:30:100
|
||||
#PermitTunnel no
|
||||
#ChrootDirectory none
|
||||
#VersionAddendum none
|
||||
|
||||
# no default banner path
|
||||
#Banner none
|
||||
|
||||
# override default of no subsystems
|
||||
Subsystem sftp /usr/lib/ssh/sftp-server
|
||||
|
||||
# Example of overriding settings on a per-user basis
|
||||
#Match User anoncvs
|
||||
# X11Forwarding no
|
||||
# AllowTcpForwarding no
|
||||
# PermitTTY no
|
||||
# ForceCommand cvs server
|
117
cmd/templates/sshd/sshd_config_permit_root
Normal file
117
cmd/templates/sshd/sshd_config_permit_root
Normal file
@ -0,0 +1,117 @@
|
||||
# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $
|
||||
|
||||
# This is the sshd server system-wide configuration file. See
|
||||
# sshd_config(5) for more information.
|
||||
|
||||
# This sshd was compiled with PATH=/bin:/usr/bin:/sbin:/usr/sbin
|
||||
|
||||
# The strategy used for options in the default sshd_config shipped with
|
||||
# OpenSSH is to specify options with their default value where
|
||||
# possible, but leave them commented. Uncommented options override the
|
||||
# default value.
|
||||
|
||||
#Port 22
|
||||
#AddressFamily any
|
||||
#ListenAddress 0.0.0.0
|
||||
#ListenAddress ::
|
||||
|
||||
#HostKey /etc/ssh/ssh_host_rsa_key
|
||||
#HostKey /etc/ssh/ssh_host_ecdsa_key
|
||||
#HostKey /etc/ssh/ssh_host_ed25519_key
|
||||
|
||||
# Ciphers and keying
|
||||
#RekeyLimit default none
|
||||
|
||||
# Logging
|
||||
#SyslogFacility AUTH
|
||||
#LogLevel INFO
|
||||
|
||||
# Authentication:
|
||||
|
||||
#LoginGraceTime 2m
|
||||
PermitRootLogin yes
|
||||
#StrictModes yes
|
||||
#MaxAuthTries 6
|
||||
#MaxSessions 10
|
||||
|
||||
#PubkeyAuthentication yes
|
||||
|
||||
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
|
||||
# but this is overridden so installations will only check .ssh/authorized_keys
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
|
||||
#AuthorizedPrincipalsFile none
|
||||
|
||||
#AuthorizedKeysCommand none
|
||||
#AuthorizedKeysCommandUser nobody
|
||||
|
||||
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
|
||||
#HostbasedAuthentication no
|
||||
# Change to yes if you don't trust ~/.ssh/known_hosts for
|
||||
# HostbasedAuthentication
|
||||
#IgnoreUserKnownHosts no
|
||||
# Don't read the user's ~/.rhosts and ~/.shosts files
|
||||
#IgnoreRhosts yes
|
||||
|
||||
# To disable tunneled clear text passwords, change to no here!
|
||||
#PasswordAuthentication yes
|
||||
#PermitEmptyPasswords no
|
||||
|
||||
# Change to no to disable s/key passwords
|
||||
#ChallengeResponseAuthentication yes
|
||||
|
||||
# Kerberos options
|
||||
#KerberosAuthentication no
|
||||
#KerberosOrLocalPasswd yes
|
||||
#KerberosTicketCleanup yes
|
||||
#KerberosGetAFSToken no
|
||||
|
||||
# GSSAPI options
|
||||
#GSSAPIAuthentication no
|
||||
#GSSAPICleanupCredentials yes
|
||||
|
||||
# Set this to 'yes' to enable PAM authentication, account processing,
|
||||
# and session processing. If this is enabled, PAM authentication will
|
||||
# be allowed through the ChallengeResponseAuthentication and
|
||||
# PasswordAuthentication. Depending on your PAM configuration,
|
||||
# PAM authentication via ChallengeResponseAuthentication may bypass
|
||||
# the setting of "PermitRootLogin without-password".
|
||||
# If you just want the PAM account and session checks to run without
|
||||
# PAM authentication, then enable this but set PasswordAuthentication
|
||||
# and ChallengeResponseAuthentication to 'no'.
|
||||
#UsePAM no
|
||||
|
||||
#AllowAgentForwarding yes
|
||||
# Feel free to re-enable these if your use case requires them.
|
||||
AllowTcpForwarding no
|
||||
GatewayPorts no
|
||||
X11Forwarding no
|
||||
#X11DisplayOffset 10
|
||||
#X11UseLocalhost yes
|
||||
#PermitTTY yes
|
||||
#PrintMotd yes
|
||||
#PrintLastLog yes
|
||||
#TCPKeepAlive yes
|
||||
#PermitUserEnvironment no
|
||||
#Compression delayed
|
||||
#ClientAliveInterval 0
|
||||
#ClientAliveCountMax 3
|
||||
#UseDNS no
|
||||
#PidFile /run/sshd.pid
|
||||
#MaxStartups 10:30:100
|
||||
#PermitTunnel no
|
||||
#ChrootDirectory none
|
||||
#VersionAddendum none
|
||||
|
||||
# no default banner path
|
||||
#Banner none
|
||||
|
||||
# override default of no subsystems
|
||||
Subsystem sftp /usr/lib/ssh/sftp-server
|
||||
|
||||
# Example of overriding settings on a per-user basis
|
||||
#Match User anoncvs
|
||||
# X11Forwarding no
|
||||
# AllowTcpForwarding no
|
||||
# PermitTTY no
|
||||
# ForceCommand cvs server
|
47
cmd/unpack.go
Normal file
47
cmd/unpack.go
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright © 2022 Lukas Bachschwell <lukas@lbsfilm.at>
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// unpackCmd represents the unpack command
|
||||
var unpackCmd = &cobra.Command{
|
||||
Use: "unpack",
|
||||
Short: "Unpack the overlay file to a new workdir",
|
||||
Long: `Unpack the overlay file to a new workdir`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("unpack called")
|
||||
prepareWorkdir()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(unpackCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// unpackCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// unpackCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
Reference in New Issue
Block a user