inital
This commit is contained in:
commit
e03def9d03
19 changed files with 1015 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
test
|
||||||
|
crtman.toml
|
||||||
0
LICENSE
Normal file
0
LICENSE
Normal file
40
cmd/get.go
Normal file
40
cmd/get.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getCmd represents the get command
|
||||||
|
var getCmd = &cobra.Command{
|
||||||
|
Use: "get",
|
||||||
|
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("get called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(getCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// getCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// getCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
getCmd.Flags().Bool("ca", false, "Get CA certificate")
|
||||||
|
}
|
||||||
48
cmd/root.go
Normal file
48
cmd/root.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cfgFilePath string
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "crtman",
|
||||||
|
Short: "A SOHO Certification System",
|
||||||
|
Long: `This Application is made for TCIVS CSE Competitors' Room.`,
|
||||||
|
// 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() *pflag.FlagSet {
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return rootCmd.Flags()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// 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(&cfgFilePath, "config", "", "config file (default is $HOME/.crtman.yaml)")
|
||||||
|
|
||||||
|
// Cobra also supports local flags, which will only run
|
||||||
|
// when this action is called directly.
|
||||||
|
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
rootCmd.PersistentFlags().StringP("host", "c", "", "Remote/Listen Host IP")
|
||||||
|
rootCmd.PersistentFlags().StringP("port", "p", "", "Remote/Listen Host Port")
|
||||||
|
}
|
||||||
89
cmd/serve.go
Normal file
89
cmd/serve.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/jasinco/crtman/internal/cli"
|
||||||
|
"github.com/jasinco/crtman/internal/crt"
|
||||||
|
"github.com/jasinco/crtman/internal/store"
|
||||||
|
"github.com/jasinco/crtman/internal/web"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var init_prog, reinit bool
|
||||||
|
|
||||||
|
// serveCmd represents the serve command
|
||||||
|
var serveCmd = &cobra.Command{
|
||||||
|
Use: "serve",
|
||||||
|
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("serve called")
|
||||||
|
|
||||||
|
viper.BindPFlag("ca.organization", cmd.Flags().Lookup("ca_org"))
|
||||||
|
viper.BindPFlag("ca.fqdn", cmd.Flags().Lookup("ca_fqdn"))
|
||||||
|
viper.BindPFlags(cmd.Flags())
|
||||||
|
|
||||||
|
// config handle
|
||||||
|
err := viper.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("can't load config %e\n", err)
|
||||||
|
}
|
||||||
|
cli.GetListen()
|
||||||
|
cli.GetPersistent()
|
||||||
|
log.Printf("Get Persistent Path: %s", cli.PersistentPath)
|
||||||
|
store.InitStore(cli.PersistentPath)
|
||||||
|
defer store.CloseStore()
|
||||||
|
|
||||||
|
check_init := store.ChekInit()
|
||||||
|
|
||||||
|
if init_prog {
|
||||||
|
if check_init && !reinit {
|
||||||
|
log.Fatalln("you have initiazlized the program.")
|
||||||
|
}
|
||||||
|
cli.GetCA_CFG()
|
||||||
|
crt.IssueRoot(cli.CA_config)
|
||||||
|
store.Serial = *big.NewInt(1)
|
||||||
|
store.InitStoreData()
|
||||||
|
check_init = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Root CA
|
||||||
|
if !check_init {
|
||||||
|
log.Fatalln("Hasn't Initialized, CA_cert not found")
|
||||||
|
}
|
||||||
|
cli.ParseFederation()
|
||||||
|
|
||||||
|
web.Listen(cli.Host, cli.Port)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(serveCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// serveCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// serveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
serveCmd.Flags().StringP("store", "s", "", "The place the appstore its persistent stuff")
|
||||||
|
serveCmd.Flags().BoolVar(&init_prog, "init", false, "Init the settings, create CA, ...etc")
|
||||||
|
serveCmd.Flags().BoolVar(&reinit, "reinit", false, "It reinit the whole chain(ca, keys)")
|
||||||
|
serveCmd.Flags().String("ca_org", "", "the CA's organization")
|
||||||
|
serveCmd.Flags().String("ca_fqdn", "", "the CA's FQDN")
|
||||||
|
}
|
||||||
22
crtman.template.toml
Normal file
22
crtman.template.toml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
store="./test.db`"
|
||||||
|
|
||||||
|
[ca]
|
||||||
|
organization=""
|
||||||
|
fqdn=""
|
||||||
|
|
||||||
|
[federation]
|
||||||
|
backend="ldap"
|
||||||
|
auth=["ldap"]
|
||||||
|
|
||||||
|
# this ldap config is for LLDAP
|
||||||
|
[federation.ldap]
|
||||||
|
auth_user = "uid=admin,ou=people,dc=tcivs,dc=intra"
|
||||||
|
auth_password= ""
|
||||||
|
base_url="ldap://localhost:3890"
|
||||||
|
# user
|
||||||
|
user_identify = "uid"
|
||||||
|
user_objectclass = "persons"
|
||||||
|
#group
|
||||||
|
group_identify = "cn"
|
||||||
|
group_objectclass = "groups"
|
||||||
|
group_base_dn = "ou=group,dc=tcivs,dc=intra"
|
||||||
65
go.mod
Normal file
65
go.mod
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
module github.com/jasinco/crtman
|
||||||
|
|
||||||
|
go 1.25.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
|
github.com/abcum/lcp v0.0.0-20201209214815-7a3f3840be81 // indirect
|
||||||
|
github.com/bytedance/sonic v1.14.0 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
|
github.com/gin-gonic/gin v1.11.0 // indirect
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.12 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||||
|
github.com/gofrs/flock v0.8.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/go-immutable-radix/v2 v2.0.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mattetti/filebuffer v1.0.1 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/cobra v1.10.1 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/spf13/viper v1.21.0 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
|
go.mills.io/bitcask/v2 v2.1.3 // indirect
|
||||||
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/arch v0.20.0 // indirect
|
||||||
|
golang.org/x/crypto v0.43.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||||
|
golang.org/x/mod v0.28.0 // indirect
|
||||||
|
golang.org/x/net v0.45.0 // indirect
|
||||||
|
golang.org/x/sync v0.17.0 // indirect
|
||||||
|
golang.org/x/sys v0.37.0 // indirect
|
||||||
|
golang.org/x/text v0.30.0 // indirect
|
||||||
|
golang.org/x/tools v0.37.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.9 // indirect
|
||||||
|
)
|
||||||
166
go.sum
Normal file
166
go.sum
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
|
github.com/abcum/lcp v0.0.0-20201209214815-7a3f3840be81 h1:uHogIJ9bXH75ZYrXnVShHIyywFiUZ7OOabwd9Sfd8rw=
|
||||||
|
github.com/abcum/lcp v0.0.0-20201209214815-7a3f3840be81/go.mod h1:6ZvnjTZX1LNo1oLpfaJK8h+MXqHxcBFBIwkgsv+xlv0=
|
||||||
|
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||||
|
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||||
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
|
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||||
|
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
|
github.com/go-ldap/ldap v3.4.12+incompatible h1:rxzUa+YRcrq19g6GYBUHsYVKSVIiY2uvprRFCdR/t5A=
|
||||||
|
github.com/go-ldap/ldap v3.4.12+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||||
|
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
|
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||||
|
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/hashicorp/go-immutable-radix/v2 v2.0.0 h1:nq9lQ5I71Heg2lRb2/+szuIWKY3Y73d8YKyXyN91WzU=
|
||||||
|
github.com/hashicorp/go-immutable-radix/v2 v2.0.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM=
|
||||||
|
github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
|
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||||
|
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||||
|
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||||
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||||
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
|
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||||
|
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||||
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||||
|
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
|
go.mills.io/bitcask/v2 v2.1.3 h1:xOe0sdkTHqgpMxsfDjcpaWOm8VtCmA5JzRy4dbicFfk=
|
||||||
|
go.mills.io/bitcask/v2 v2.1.3/go.mod h1:ZQFykoTTCvMwy24lBstZhSRQuleYIB4EzWKSOgEv6+k=
|
||||||
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||||
|
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
|
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||||
|
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||||
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||||
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
|
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||||
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
|
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||||
|
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
|
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||||
|
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||||
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
|
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||||
|
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||||
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
|
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||||
|
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||||
|
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||||
|
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
8
internal/accounting/common.go
Normal file
8
internal/accounting/common.go
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
package accounting
|
||||||
|
|
||||||
|
type Account interface {
|
||||||
|
IsUser(name string) bool
|
||||||
|
IsGroup(name string) bool
|
||||||
|
UserInGroup(user string, group string) bool
|
||||||
|
AuthUser(name string, password string) bool
|
||||||
|
}
|
||||||
117
internal/accounting/ldap_fed.go
Normal file
117
internal/accounting/ldap_fed.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
package accounting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LDAP_Config struct {
|
||||||
|
Base_DN string `mapstructure:"base_dn"`
|
||||||
|
|
||||||
|
User_iden string `mapstructure:"user_identify"`
|
||||||
|
User_objclass string `mapstructure:"user_objectclass"`
|
||||||
|
|
||||||
|
Group_iden string `mapstructure:"group_identify"`
|
||||||
|
Group_base string `mapstructure:"group_base_dn"`
|
||||||
|
Group_objclass string `mapstructure:"group_objectclass"`
|
||||||
|
|
||||||
|
Auth_username string `mapstructure:"auth_user"`
|
||||||
|
Auth_password string `mapstructure:"auth_password"`
|
||||||
|
Base_URL string `mapstructure:"base_url"`
|
||||||
|
}
|
||||||
|
type LDAP struct {
|
||||||
|
conn *ldap.Conn
|
||||||
|
cfg LDAP_Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) build_query(filter string) *ldap.SearchRequest {
|
||||||
|
return ldap.NewSearchRequest(l.cfg.Base_DN,
|
||||||
|
ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
|
||||||
|
filter,
|
||||||
|
[]string{"dn"}, nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) IsUser(name string) bool {
|
||||||
|
req := l.build_query(
|
||||||
|
fmt.Sprintf("(&(objectClass=%s)(%s=%s))",
|
||||||
|
l.cfg.User_objclass, l.cfg.User_iden, ldap.EscapeFilter(name),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sr, err := l.conn.Search(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(sr.Entries) != 1 {
|
||||||
|
log.Println("Found multiple entries for single user")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) IsGroup(name string) bool {
|
||||||
|
log.Printf("LDAP: Checking if group %s exists...\n", name)
|
||||||
|
req := l.build_query(
|
||||||
|
fmt.Sprintf("(&(objectClass=%s)(%s=%s))",
|
||||||
|
l.cfg.Group_objclass, l.cfg.Group_iden, ldap.EscapeFilter(name),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sr, err := l.conn.Search(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(sr.Entries) != 1 {
|
||||||
|
log.Println("Found multiple entries for single group")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) UserInGroup(user string, group string) bool {
|
||||||
|
log.Printf("LDAP: Checking if user %s is in group %s...\n", user, group)
|
||||||
|
|
||||||
|
req := l.build_query(
|
||||||
|
fmt.Sprintf("(&(objectClass=%s)(%s=%s)(memberof=%s=%s,%s))",
|
||||||
|
l.cfg.User_objclass,
|
||||||
|
l.cfg.User_iden,
|
||||||
|
ldap.EscapeFilter(user),
|
||||||
|
l.cfg.Group_iden,
|
||||||
|
ldap.EscapeFilter(group),
|
||||||
|
l.cfg.Group_base,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
sr, err := l.conn.Search(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(sr.Entries) != 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) AuthUser(name string, password string) bool {
|
||||||
|
log.Printf("LDAP: Attempting authentication for user %s...\n", name)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLDAP(config LDAP_Config) LDAP {
|
||||||
|
new_dial, err := ldap.DialURL(config.Base_URL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = new_dial.Bind(config.Auth_username, config.Auth_password)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return LDAP{
|
||||||
|
conn: new_dial,
|
||||||
|
cfg: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
49
internal/cli/federation.go
Normal file
49
internal/cli/federation.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/jasinco/crtman/internal/accounting"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var backend accounting.Account
|
||||||
|
var auth_backend map[string]*accounting.Account
|
||||||
|
|
||||||
|
func ParseFederation() {
|
||||||
|
log.Println("Getting Federation")
|
||||||
|
account_backend := viper.GetString("federation.backend")
|
||||||
|
log.Printf("Accounting: %s\n", account_backend)
|
||||||
|
auth := viper.GetStringSlice("federation.auth")
|
||||||
|
log.Println("Authenticate", auth)
|
||||||
|
|
||||||
|
backend = construct(account_backend)
|
||||||
|
|
||||||
|
auth_backend = make(map[string]*accounting.Account)
|
||||||
|
for _, method := range auth {
|
||||||
|
if method == account_backend {
|
||||||
|
auth_backend[method] = &backend
|
||||||
|
} else {
|
||||||
|
auth := construct(method)
|
||||||
|
auth_backend[method] = &auth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func construct_ldap() accounting.LDAP {
|
||||||
|
block_name := "federation.ldap"
|
||||||
|
var ldap_cfg accounting.LDAP_Config
|
||||||
|
viper.UnmarshalKey(block_name, &ldap_cfg)
|
||||||
|
return accounting.NewLDAP(ldap_cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func construct(name string) accounting.Account {
|
||||||
|
var account accounting.Account
|
||||||
|
switch name {
|
||||||
|
case "ldap":
|
||||||
|
ldap_inst := construct_ldap()
|
||||||
|
account = &ldap_inst
|
||||||
|
default:
|
||||||
|
log.Fatal("No Federation")
|
||||||
|
}
|
||||||
|
return account
|
||||||
|
}
|
||||||
44
internal/cli/helper.go
Normal file
44
internal/cli/helper.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CA_CFG struct {
|
||||||
|
Org string
|
||||||
|
FQDN string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Host string
|
||||||
|
var Port string
|
||||||
|
var PersistentPath string
|
||||||
|
var CA_config CA_CFG
|
||||||
|
|
||||||
|
// the real endpoint that you should access the server
|
||||||
|
// e.g. 172.16.0.1:3232
|
||||||
|
var Outbound string
|
||||||
|
|
||||||
|
func GetListen() {
|
||||||
|
viper.SetDefault("host", "localhost")
|
||||||
|
viper.SetDefault("port", "8086")
|
||||||
|
Host = viper.GetString("host")
|
||||||
|
Port = viper.GetString("port")
|
||||||
|
viper.SetDefault("outbound", fmt.Sprintf("%s:%s", Host, Port))
|
||||||
|
Outbound = viper.GetString("outbound")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPersistent() {
|
||||||
|
viper.SetDefault("store", "/tmp/db")
|
||||||
|
PersistentPath = viper.GetString("store")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCA_CFG() {
|
||||||
|
viper.SetDefault("ca.organization", "TCIVS_CSE_CR")
|
||||||
|
viper.SetDefault("ca.fqdn", "example.com")
|
||||||
|
CA_config = CA_CFG{
|
||||||
|
Org: viper.GetString("ca.organization"),
|
||||||
|
FQDN: viper.GetString("ca.fqdn"),
|
||||||
|
}
|
||||||
|
}
|
||||||
40
internal/crt/cert.go
Normal file
40
internal/crt/cert.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package crt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/jasinco/crtman/internal/store"
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckValid(req *ocsp.Request) sql.Null[ocsp.Response] {
|
||||||
|
var response ocsp.Response
|
||||||
|
response.Status = ocsp.Revoked
|
||||||
|
if !req.HashAlgorithm.Available() {
|
||||||
|
return sql.Null[ocsp.Response]{Valid: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
leaf := store.GetLeafCert(req.SerialNumber)
|
||||||
|
if !leaf.Valid {
|
||||||
|
return sql.Null[ocsp.Response]{Valid: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher := req.HashAlgorithm.New()
|
||||||
|
valid_issuer_dn := bytes.Equal(hasher.Sum(leaf.V.Cert.RawIssuer), req.IssuerKeyHash)
|
||||||
|
hasher.Reset()
|
||||||
|
valid_issuer_key := bytes.Equal(hasher.Sum(leaf.V.Cert.RawSubjectPublicKeyInfo), req.IssuerKeyHash)
|
||||||
|
|
||||||
|
if !(valid_issuer_dn && valid_issuer_key) {
|
||||||
|
response.Status = ocsp.Unknown
|
||||||
|
}
|
||||||
|
response.SerialNumber = req.SerialNumber
|
||||||
|
|
||||||
|
if !leaf.V.RevokeAt.Valid {
|
||||||
|
response.Status = ocsp.Good
|
||||||
|
} else {
|
||||||
|
response.RevokedAt = leaf.V.RevokeAt.Time
|
||||||
|
response.RevocationReason = int(leaf.V.RevokeReason.Int16)
|
||||||
|
}
|
||||||
|
return sql.Null[ocsp.Response]{Valid: true, V: response}
|
||||||
|
}
|
||||||
92
internal/crt/issue.go
Normal file
92
internal/crt/issue.go
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
package crt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jasinco/crtman/internal/cli"
|
||||||
|
"github.com/jasinco/crtman/internal/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IssueRoot(config cli.CA_CFG) {
|
||||||
|
log.Printf("CA: Org: %s, FQDN: %s\n", config.Org, config.FQDN)
|
||||||
|
rootca := x509.Certificate{
|
||||||
|
IsCA: true,
|
||||||
|
Version: 1,
|
||||||
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning},
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Country: []string{"Taiwan"},
|
||||||
|
Organization: []string{config.Org},
|
||||||
|
CommonName: config.FQDN,
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
SerialNumber: big.NewInt(0),
|
||||||
|
}
|
||||||
|
privkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
crt, err := x509.CreateCertificate(rand.Reader, &rootca, &rootca, &privkey.PublicKey, privkey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("can't create x509 ca, ", err)
|
||||||
|
}
|
||||||
|
// privkey_bytes, err := x509.MarshalECPrivateKey(privkey)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal("can't create ecdsa key encoded, ", err)
|
||||||
|
// }
|
||||||
|
store.RootCA = crt
|
||||||
|
store.RootCAKey = privkey
|
||||||
|
}
|
||||||
|
|
||||||
|
// csr is a DER csr
|
||||||
|
func IssueCert(csr []byte) sql.Null[[]byte] {
|
||||||
|
req, err := x509.ParseCertificateRequest(csr)
|
||||||
|
if err != nil {
|
||||||
|
return sql.Null[[]byte]{Valid: false}
|
||||||
|
}
|
||||||
|
if req.CheckSignature() != nil {
|
||||||
|
return sql.Null[[]byte]{Valid: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
store.SerialLock.Lock()
|
||||||
|
defer store.SerialLock.Unlock()
|
||||||
|
cert := x509.Certificate{
|
||||||
|
Subject: req.Subject,
|
||||||
|
PublicKey: req.PublicKey,
|
||||||
|
PublicKeyAlgorithm: req.PublicKeyAlgorithm,
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(time.Hour * 24 * 720),
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
IsCA: false,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
DNSNames: req.DNSNames,
|
||||||
|
IPAddresses: req.IPAddresses,
|
||||||
|
SerialNumber: &store.Serial,
|
||||||
|
OCSPServer: []string{fmt.Sprintf("https://%s/api/ocsp", cli.Outbound)},
|
||||||
|
}
|
||||||
|
ca, err := x509.ParseCertificate(store.RootCA)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return sql.Null[[]byte]{Valid: false}
|
||||||
|
}
|
||||||
|
signed, err := x509.CreateCertificate(rand.Reader, &cert, ca, req.PublicKey, store.RootCAKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return sql.Null[[]byte]{Valid: false}
|
||||||
|
}
|
||||||
|
store.Serial.Add(&store.Serial, big.NewInt(1))
|
||||||
|
|
||||||
|
return sql.Null[[]byte]{Valid: true, V: signed}
|
||||||
|
}
|
||||||
123
internal/store/storage.go
Normal file
123
internal/store/storage.go
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *sql.DB
|
||||||
|
var RootCA []byte
|
||||||
|
var RootCAKey *ecdsa.PrivateKey
|
||||||
|
var SerialLock sync.Mutex
|
||||||
|
var Serial big.Int
|
||||||
|
|
||||||
|
func InitStore(path string) {
|
||||||
|
var err error
|
||||||
|
DB, err = sql.Open("sqlite3", path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func CloseStore() {
|
||||||
|
DB.Close()
|
||||||
|
}
|
||||||
|
func ChekInit() bool {
|
||||||
|
result := DB.QueryRow("select id,cert,key,serial from syscfg order by id desc limit 1")
|
||||||
|
if result.Err() != nil {
|
||||||
|
log.Println(result.Err())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var placeholder int
|
||||||
|
var temp_serial, temp_ca_key []byte
|
||||||
|
err := result.Scan(&placeholder, &RootCA, &temp_ca_key, &temp_serial)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Serial.SetBytes(temp_serial)
|
||||||
|
RootCAKey, err = x509.ParseECPrivateKey(temp_ca_key)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func initSchema(tx *sql.Tx) {
|
||||||
|
_, err := tx.Exec(`
|
||||||
|
create table syscfg (id integer not null primary key autoincrement, cert blob not null, key blob not null, serial blob not null);
|
||||||
|
delete from syscfg;
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = tx.Exec(`
|
||||||
|
create table leaf (id blob not null primary key, cert blob not null, owner text not null, revokeAt datetime, revokeReason integer);
|
||||||
|
delete from leaf;
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
func InitStoreData() {
|
||||||
|
ctx := context.Background()
|
||||||
|
tx, err := DB.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
initSchema(tx)
|
||||||
|
|
||||||
|
temp_ca_key, err := x509.MarshalECPrivateKey(RootCAKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Exec(`
|
||||||
|
insert into syscfg(cert,key,serial) values(?,?,?)
|
||||||
|
`, RootCA, temp_ca_key, Serial.Bytes())
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func InsertCert(cert []byte, applicant string) {
|
||||||
|
parse, err := x509.ParseCertificate(cert)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DB.Exec("insert into syscfg(id,cert,owner) values(?,?,?)", parse.SerialNumber.Bytes(), cert, applicant)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LeafCert struct {
|
||||||
|
Cert_org []byte
|
||||||
|
Cert *x509.Certificate
|
||||||
|
RevokeAt sql.NullTime
|
||||||
|
RevokeReason sql.NullInt16
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLeafCert(serial_num *big.Int) sql.Null[LeafCert] {
|
||||||
|
var err error
|
||||||
|
result := DB.QueryRow("select cert,revokeAt, revokeReason from leaf where id = ?", serial_num.Bytes())
|
||||||
|
var leaf LeafCert
|
||||||
|
if result.Err() != nil {
|
||||||
|
log.Println(result.Err())
|
||||||
|
return sql.Null[LeafCert]{Valid: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = result.Scan(&leaf.Cert_org, &leaf.RevokeAt, &leaf.RevokeReason)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return sql.Null[LeafCert]{Valid: false}
|
||||||
|
}
|
||||||
|
leaf.Cert, err = x509.ParseCertificate(leaf.Cert_org)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return sql.Null[LeafCert]{Valid: false}
|
||||||
|
}
|
||||||
|
return sql.Null[LeafCert]{V: leaf, Valid: true}
|
||||||
|
|
||||||
|
}
|
||||||
56
internal/web/ocsp.go
Normal file
56
internal/web/ocsp.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/jasinco/crtman/internal/crt"
|
||||||
|
"github.com/jasinco/crtman/internal/store"
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// comply to https://datatracker.ietf.org/doc/html/rfc6960#appendix-A
|
||||||
|
func ocsp_handling(c *gin.Context) {
|
||||||
|
var req_bin []byte
|
||||||
|
var err error
|
||||||
|
if c.Request.Method == "GET" {
|
||||||
|
b64_req := c.Param("req")
|
||||||
|
req_bin, err = base64.RawURLEncoding.DecodeString(b64_req)
|
||||||
|
if err != nil {
|
||||||
|
c.Status(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req_bin, err = io.ReadAll(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.Status(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req, err := ocsp.ParseRequest(req_bin)
|
||||||
|
if err != nil {
|
||||||
|
c.Status(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result := crt.CheckValid(req)
|
||||||
|
if !result.Valid {
|
||||||
|
c.Status(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ca, err := x509.ParseCertificate(store.RootCA)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
c.Status(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response, err := ocsp.CreateResponse(ca, ca, result.V, store.RootCAKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
c.Status(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data(200, "application/ocsp-response", response)
|
||||||
|
}
|
||||||
29
internal/web/serve.go
Normal file
29
internal/web/serve.go
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/jasinco/crtman/internal/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Listen(host string, port string) {
|
||||||
|
r := gin.Default()
|
||||||
|
r.GET("/api/rootca.pem", func(ctx *gin.Context) {
|
||||||
|
ca_pem := pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: store.RootCA,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data(200, "application/x-x509-ca-cert", pem.EncodeToMemory(&ca_pem))
|
||||||
|
})
|
||||||
|
r.GET("/api/ocsp/:req", ocsp_handling)
|
||||||
|
r.POST("/api/ocsp", ocsp_handling)
|
||||||
|
|
||||||
|
err := r.Run(fmt.Sprintf("%s:%s", host, port))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
justfile
Normal file
4
justfile
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
run:
|
||||||
|
CGO_ENABLED=1 go run ./main.go serve
|
||||||
|
serveinit:
|
||||||
|
CGO_ENABLED=1 go run ./main.go serve --init
|
||||||
21
main.go
Normal file
21
main.go
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jasinco/crtman/cmd"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
viper.SetConfigName("crtman.toml")
|
||||||
|
viper.AddConfigPath("/etc/")
|
||||||
|
viper.AddConfigPath("$HOME/")
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.SetConfigType("toml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue