Commit 4d16f752 authored by Heiko Reese's avatar Heiko Reese
Browse files

Mote adventures into custom cmdline argument types

parent 02ad18fc
package cmd
import (
"errors"
"log"
"net"
"net/url"
"strings"
)
// TODO: broken
func IPSliceToStrings(ip interface{}) []string {
if v, ok := ip.([]net.IP); ok {
iplist := make([]string, 0, len(v))
for _, ip := range v {
iplist = append(iplist, ip.String())
}
return iplist
} else {
log.Fatal(ok)
}
log.Fatal("Unable to convert ip addresses")
return nil
}
// URLSlice stores urls from cmdline arguments
type URLSlice []*url.URL
func (u *URLSlice) String() string {
urls := make([]string, 0, len(*u))
for _, uri := range *u {
urls = append(urls, uri.String())
}
return strings.Join(urls, ",")
}
func (u *URLSlice) Set(rawurl string) error {
for _, singleurl := range strings.Split(rawurl, ",") {
uri, err := url.Parse(singleurl)
if err != nil {
return err
}
*u = append(*u, uri)
}
return nil
}
func (u *URLSlice) Type() string {
return "URLSlice"
}
// IPSlice stores ip addresses from cmdline arguments
type IPSLice []*net.IP
func (i *IPSLice) String() string {
ips := make([]string, 0, len(*i))
for _, ip := range *i {
ips = append(ips, ip.String())
}
return strings.Join(ips, ",")
}
func (i *IPSLice) Set(ipstring string) error {
for _, singleip := range strings.Split(ipstring, ",") {
ip := net.ParseIP(singleip)
if ip == nil {
return errors.New("Unable to parse IP " + singleip)
}
*i = append(*i, &ip)
}
return nil
}
func (i *IPSLice) Type() string {
return "IPSlice"
}
...@@ -43,31 +43,6 @@ type sanGenerator struct { ...@@ -43,31 +43,6 @@ type sanGenerator struct {
Values []string Values []string
} }
var requestArgs = struct {
Profile string
CommonName string
Organisation string
OU []string
Locality string
State string
Country string
SANDNS []string
SANIP []net.IP
SANURI []string
SANEmail []string
RequesterName string
RequesterEmail string
RequesterOU string
Keyfile string
Keypass string
Keysize int
PIN string
Outdir string
SkipPDF bool
Publish bool
DryRun bool
}{}
// requestCmd represents the request command // requestCmd represents the request command
var requestCmd *cobra.Command = &cobra.Command{ var requestCmd *cobra.Command = &cobra.Command{
Use: "request", Use: "request",
...@@ -76,10 +51,18 @@ var requestCmd *cobra.Command = &cobra.Command{ ...@@ -76,10 +51,18 @@ var requestCmd *cobra.Command = &cobra.Command{
TraverseChildren: true, TraverseChildren: true,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var ( var (
err error err error
pkey *rsa.PrivateKey pkey *rsa.PrivateKey
commonName string
profile string
basefilename string
outputdir string
) )
viper.BindPFlags(cmd.PersistentFlags()) viper.BindPFlags(cmd.PersistentFlags())
//spew.Dump(viper.AllSettings())
//spew.Dump(cmd.Args)
//spew.Dump(cmdArguments)
//os.Exit(0)
// check ca name // check ca name
if !viper.IsSet("ca") { if !viper.IsSet("ca") {
...@@ -90,10 +73,9 @@ var requestCmd *cobra.Command = &cobra.Command{ ...@@ -90,10 +73,9 @@ var requestCmd *cobra.Command = &cobra.Command{
if viper.GetString("cn") == "" { if viper.GetString("cn") == "" {
log.Fatal("Please specify a common name (»CN«)") log.Fatal("Please specify a common name (»CN«)")
} }
// clean up CommonName // clean up CommonName
cnCleanUp := strings.NewReplacer("Ä", "Ae", "Ö", "Oe", "Ü", "Ue", "ä", "ae", "ö", "oe", "ü", "ue", "ß", "ss") cnCleanUp := strings.NewReplacer("Ä", "Ae", "Ö", "Oe", "Ü", "Ue", "ä", "ae", "ö", "oe", "ü", "ue", "ß", "ss")
commonName := viper.GetString("cn") commonName = viper.GetString("cn")
commonNameCleaned := cnCleanUp.Replace(commonName) commonNameCleaned := cnCleanUp.Replace(commonName)
if commonNameCleaned != viper.GetString("cn") { if commonNameCleaned != viper.GetString("cn") {
log.Printf("Transcribed illegal characters in cn »%s«: »%s«", commonName, commonNameCleaned) log.Printf("Transcribed illegal characters in cn »%s«: »%s«", commonName, commonNameCleaned)
...@@ -102,16 +84,16 @@ var requestCmd *cobra.Command = &cobra.Command{ ...@@ -102,16 +84,16 @@ var requestCmd *cobra.Command = &cobra.Command{
} }
// check profile // check profile
profile := viper.GetString("profile") profile = viper.GetString("profile")
dfnrole, exists := dfnpki.DFNPublicRoles[profile] dfnrole, exists := dfnpki.DFNPublicRoles[profile]
if !exists { if !exists {
log.Fatal("Unknown profile ", profile) log.Fatal("Unknown profile ", profile)
} }
// set default outpath // set default outpath
basefilename := sanitize.Name(commonName) basefilename = sanitize.Name(commonName)
timestamp := time.Now().Format("_2006-02-01T15_04_05") timestamp := time.Now().Format("_2006-02-01T15_04_05")
outputdir := viper.GetString("outdir") outputdir = viper.GetString("outdir")
// create output path // create output path
if outputdir == "" { if outputdir == "" {
...@@ -195,61 +177,50 @@ var requestCmd *cobra.Command = &cobra.Command{ ...@@ -195,61 +177,50 @@ var requestCmd *cobra.Command = &cobra.Command{
}, },
SignatureAlgorithm: x509.SHA256WithRSA, SignatureAlgorithm: x509.SHA256WithRSA,
} }
spew.Dump(viper.IsSet("ou"))
// OU // OU
if viper.IsSet("ou") { if viper.IsSet("ou") {
request.Subject.OrganizationalUnit = viper.GetStringSlice("ou") request.Subject.OrganizationalUnit = viper.GetStringSlice("ou")
} }
// O // O
if viper.IsSet("o") { if viper.GetString("o") != "" {
request.Subject.Organization = []string{viper.GetString("o")} request.Subject.Organization = []string{viper.GetString("o")}
} else {
log.Fatal("Please specify Organization (»O«)")
} }
// L // L
if viper.IsSet("locality") { if viper.GetString("locality") != "" {
request.Subject.Locality = []string{viper.GetString("locality")} request.Subject.Locality = []string{viper.GetString("locality")}
} }
// ST // ST
if viper.IsSet("state") { if viper.GetString("state") != "" {
request.Subject.Province = []string{viper.GetString("state")} request.Subject.Province = []string{viper.GetString("state")}
} }
// C // C
if viper.IsSet("country") { if viper.GetString("country") != "" {
request.Subject.Country = []string{viper.GetString("country")} request.Subject.Country = []string{viper.GetString("country")}
} else {
log.Fatal("Please specify Country (»C«)")
} }
// DNS SAN // DNS SAN
if viper.IsSet("dns") { if viper.IsSet("dns") {
request.DNSNames = viper.GetStringSlice("dns") request.DNSNames = viper.GetStringSlice("dns")
} }
// email // email SAN
if viper.IsSet("email") { if viper.IsSet("email") {
request.EmailAddresses = viper.GetStringSlice("email") request.EmailAddresses = viper.GetStringSlice("email")
} }
// add IP SANs // IP SAN
var saniplist []net.IP
if viper.IsSet("ip") { if viper.IsSet("ip") {
for _, ipstring := range viper.GetStringSlice("ip") { if ip, ok := viper.Get("ip").([]net.IP); ok {
if ip := net.ParseIP(ipstring); ip != nil { request.IPAddresses = ip
saniplist = append(saniplist, ip)
} else {
log.Fatalf("Unable to convert »%s« to ip address", ipstring)
}
} }
request.IPAddresses = saniplist
} }
// add URL SANS // add URL SANS
var sanurllist []*url.URL if viper.IsSet("uri") {
if viper.IsSet("url") { if urls, ok := viper.Get("uri").([]*url.URL); ok {
for _, urlstring := range viper.GetStringSlice("url") { request.URIs = urls
if uri, err := url.Parse(urlstring); err == nil {
sanurllist = append(sanurllist, uri)
} else {
log.Fatalf("Unable to parse »%s« as URL: %s", urlstring, err)
}
} }
request.URIs = sanurllist
} }
spew.Dump(request)
os.Exit(4)
// generate certificate request // generate certificate request
csr, err := dfnpki.GenerateRequest(pkey, request) csr, err := dfnpki.GenerateRequest(pkey, request)
...@@ -265,7 +236,6 @@ var requestCmd *cobra.Command = &cobra.Command{ ...@@ -265,7 +236,6 @@ var requestCmd *cobra.Command = &cobra.Command{
} }
// generate compatible argument for SAN addition via API // generate compatible argument for SAN addition via API
// TODO: support all types
var SubjectAlternativeNames []string var SubjectAlternativeNames []string
for _, sanGenerator := range []sanGenerator{ for _, sanGenerator := range []sanGenerator{
{ {
...@@ -277,18 +247,20 @@ var requestCmd *cobra.Command = &cobra.Command{ ...@@ -277,18 +247,20 @@ var requestCmd *cobra.Command = &cobra.Command{
Values: viper.GetStringSlice("dns"), Values: viper.GetStringSlice("dns"),
}, },
{ {
// TODO: broken
Kind: dfnpki.URI, Kind: dfnpki.URI,
Values: viper.GetStringSlice("uri"), Values: strings.Split(viper.GetString("uri"), ","),
}, },
{ {
Kind: dfnpki.IP, Kind: dfnpki.IP,
Values: viper.GetStringSlice("ip"), Values: strings.Split(viper.GetString("ip"), ","),
}, },
} { } {
for _, value := range sanGenerator.Values { for _, value := range sanGenerator.Values {
SubjectAlternativeNames = append(SubjectAlternativeNames, dfnpki.SAN(sanGenerator.Kind, value)) SubjectAlternativeNames = append(SubjectAlternativeNames, dfnpki.SAN(sanGenerator.Kind, value))
} }
} }
spew.Dump(SubjectAlternativeNames)
// fix requester date for profile User // fix requester date for profile User
if profile == "User" { if profile == "User" {
if viper.GetString("RequesterName") == "" { if viper.GetString("RequesterName") == "" {
...@@ -360,32 +332,27 @@ func init() { ...@@ -360,32 +332,27 @@ func init() {
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
requestCmd.PersistentFlags().SortFlags = false requestCmd.PersistentFlags().SortFlags = false
requestCmd.PersistentFlags().StringVar(&requestArgs.Profile, "profile", "Web Server", "Certificate profile") requestCmd.PersistentFlags().StringVar(&cmdArguments.Profile, "profile", "Web Server", "Certificate profile")
requestCmd.PersistentFlags().StringVarP(&requestArgs.CommonName, "cn", "c", "", "Set »CommonName« (common name) part of distinguished name") requestCmd.PersistentFlags().StringVar(&cmdArguments.CommonName, "cn", "", "Set »CommonName« (common name) part of distinguished name")
requestCmd.PersistentFlags().StringSliceVar(&requestArgs.OU, "ou", nil, "Set »OU« (organizational unit) part(s) of distinguished name") requestCmd.PersistentFlags().StringSliceVar(&cmdArguments.OU, "ou", nil, "Set »OU« (organizational unit) part(s) of distinguished name")
requestCmd.PersistentFlags().StringVar(&requestArgs.Organisation, "o", "", "Set »O« (organization) part of distinguished name") requestCmd.PersistentFlags().StringVar(&cmdArguments.Organisation, "o", "", "Set »O« (organization) part of distinguished name")
requestCmd.PersistentFlags().StringVar(&requestArgs.Locality, "locality", "", "Set »L« (locality) part of distinguished name") requestCmd.PersistentFlags().StringVar(&cmdArguments.Locality, "locality", "", "Set »L« (locality) part of distinguished name")
requestCmd.PersistentFlags().StringVar(&requestArgs.State, "state", "", "Set »ST« (state) part of distinguished name") requestCmd.PersistentFlags().StringVar(&cmdArguments.State, "state", "", "Set »ST« (state) part of distinguished name")
requestCmd.PersistentFlags().StringVar(&requestArgs.Country, "country", "", "Set »C« (country) part of distinguished name") requestCmd.PersistentFlags().StringVar(&cmdArguments.Country, "country", "", "Set »C« (country) part of distinguished name")
requestCmd.PersistentFlags().StringSliceVar(&cmdArguments.DNS, "dns", nil, "Add »DNS« (hostname or domain name) Subject Alternative Name(s)")
requestCmd.PersistentFlags().StringSliceVar(&requestArgs.SANDNS, "dns", nil, "Add »DNS« (hostname or domain name) Subject Alternative Name(s)") requestCmd.PersistentFlags().Var(&cmdArguments.IP, "ip", "Add »IP« (ip address) Subject Alternative Name(s)")
requestCmd.PersistentFlags().IPSliceVar(&requestArgs.SANIP, "ip", nil, "Add »IP« (ip address) Subject Alternative Name(s)") requestCmd.PersistentFlags().Var(&cmdArguments.URI, "uri", "Add »URI« Subject Alternative Name(s)")
requestCmd.PersistentFlags().StringSliceVar(&requestArgs.SANURI, "uri", nil, "Add »URI« Subject Alternative Name(s)") requestCmd.PersistentFlags().StringSliceVar(&cmdArguments.Email, "email", nil, "Add »email« Subject Alternative Name(s)")
requestCmd.PersistentFlags().StringSliceVar(&requestArgs.SANEmail, "email", nil, "Add »email« Subject Alternative Name(s)") requestCmd.PersistentFlags().StringVar(&cmdArguments.RequesterName, "RequesterName", "", "Name of requester (»Beantrager«); set to CommonName for personal certificate")
requestCmd.PersistentFlags().StringVar(&cmdArguments.RequesterEmail, "RequesterEmail", "", "E-Mail of requester (»Beantrager«)")
requestCmd.PersistentFlags().StringVar(&requestArgs.RequesterName, "RequesterName", "", "Name of requester (»Beantrager«); set to CommonName for personal certificate") requestCmd.PersistentFlags().StringVar(&cmdArguments.RequesterOU, "RequesterOU", "", "Organisational Unit of requester (»Beantrager«)")
requestCmd.PersistentFlags().StringVar(&requestArgs.RequesterEmail, "RequesterEmail", "", "E-Mail of requester (»Beantrager«)") requestCmd.PersistentFlags().BoolVar(&cmdArguments.Publish, "publish", true, "Publish certificate")
requestCmd.PersistentFlags().StringVar(&requestArgs.RequesterOU, "RequesterOU", "", "Organisational Unit of requester (»Beantrager«)") requestCmd.PersistentFlags().StringVar(&cmdArguments.PIN, "pin", "", "PIN for revocation and retrieval of unpublished certificates")
requestCmd.PersistentFlags().StringVarP(&cmdArguments.Keyfile, "keyfile", "k", "", "Read key from filename if set; autogenerated otherwise")
requestCmd.PersistentFlags().BoolVar(&requestArgs.Publish, "publish", true, "Publish certificate")
requestCmd.PersistentFlags().StringVar(&requestArgs.PIN, "pin", "", "PIN for revocation and retrieval of unpublished certificates")
requestCmd.PersistentFlags().StringVarP(&requestArgs.Keyfile, "keyfile", "k", "", "Read key from filename if set; autogenerated otherwise")
requestCmd.MarkFlagFilename("keyfile", "") requestCmd.MarkFlagFilename("keyfile", "")
requestCmd.PersistentFlags().StringVarP(&requestArgs.Keypass, "keypass", "p", "", "Password if secret key is encrypted") requestCmd.PersistentFlags().StringVarP(&cmdArguments.Keypass, "keypass", "p", "", "Password if secret key is encrypted")
requestCmd.PersistentFlags().IntVar(&requestArgs.Keysize, "keysize", 4096, "Size of secret key in bits (only used if --keyfile is not set; minimum size 2048 bits)") requestCmd.PersistentFlags().IntVar(&cmdArguments.Keysize, "keysize", 4096, "Size of secret key in bits (only used if --keyfile is not set; minimum size 2048 bits)")
requestCmd.PersistentFlags().StringVarP(&requestArgs.Outdir, "outdir", "o", "", "Directory for all output files") requestCmd.PersistentFlags().StringVarP(&cmdArguments.Outdir, "outdir", "o", "", "Directory for all output files")
requestCmd.PersistentFlags().BoolVarP(&cmdArguments.DryRun, "dry-run", "n", false, "Only show request data, don't execute anything")
requestCmd.PersistentFlags().BoolVarP(&requestArgs.DryRun, "dry-run", "n", false, "Only show request data, don't execute anything") requestCmd.PersistentFlags().BoolVarP(&cmdArguments.SkipPDF, "skip-pdf", "s", false, "Don't fetch pdf after request")
requestCmd.PersistentFlags().BoolVarP(&requestArgs.SkipPDF, "skip-pdf", "s", false, "Don't fetch pdf after request")
} }
...@@ -56,11 +56,35 @@ var ( ...@@ -56,11 +56,35 @@ var (
// has an action associated with it: // has an action associated with it:
//Run: func(cmd *cobra.Command, args []string) { //Run: func(cmd *cobra.Command, args []string) {
//}, //},
TraverseChildren: true,
} }
// see also: Zertifizierungsrichtlinie der DFN-PKI-Sicherheitsniveau Global, Version:3.8, (OID): 1.3.6.1.4.1.22177.300.1.1.4.3.8 // see also: Zertifizierungsrichtlinie der DFN-PKI-Sicherheitsniveau Global, Version:3.8, (OID): 1.3.6.1.4.1.22177.300.1.1.4.3.8
globalArgs struct { cmdArguments struct {
CAName string CAName string
RAId int RAId int
Profile string
CommonName string
Organisation string
OU []string
Locality string
State string
Country string
DNS []string
IP IPSLice
URI URLSlice
Email []string
RequesterName string
RequesterEmail string
RequesterOU string
Keyfile string
Keypass string
Keysize int
PIN string
Outdir string
SkipPDF bool
Publish bool
DryRun bool
} }
) )
...@@ -78,13 +102,12 @@ func init() { ...@@ -78,13 +102,12 @@ func init() {
rootCmd.Flags().SortFlags = false rootCmd.Flags().SortFlags = false
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/dfnpkitool.{toml,yaml,json})") rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/dfnpkitool.{toml,yaml,json})")
rootCmd.PersistentFlags().StringVarP(&globalArgs.CAName, "ca", "C", "", "Certification Authority identifier") rootCmd.PersistentFlags().StringVarP(&cmdArguments.CAName, "ca", "C", "", "Certification Authority identifier")
rootCmd.PersistentFlags().IntVarP(&globalArgs.RAId, "ra-id", "R", 0, "Sub-RA identifier (usually 0 for main RA)") rootCmd.PersistentFlags().IntVarP(&cmdArguments.RAId, "ra-id", "R", 0, "Sub-RA identifier (usually 0 for main RA)")
viper.SetEnvPrefix("DFNPKI") //viper.BindPFlag("ca", rootCmd.PersistentFlags().Lookup("ca"))
//viper.BindPFlag("raid", rootCmd.PersistentFlags().Lookup("ra-id"))
viper.BindPFlag("ca", rootCmd.PersistentFlags().Lookup("ca")) viper.BindPFlags(rootCmd.PersistentFlags())
viper.BindPFlag("raid", rootCmd.PersistentFlags().Lookup("ra-id"))
} }
// initConfig reads in config file and ENV variables if set. // initConfig reads in config file and ENV variables if set.
......
package cmd
import (
"net/url"
"strings"
)
// URLSlice stores urls from cmdline arguments
type URLSlice []*url.URL
func (u *URLSlice) String() string {
urls := make([]string, 0, len(*u))
for _, uri := range *u {
urls = append(urls, uri.String())
}
return strings.Join(urls, ", ")
}
func (u *URLSlice) Set(rawurl string) error {
uri, err := url.Parse(rawurl)
if err != nil {
return err
}
*u = append(*u, uri)
return nil
}
func (u *URLSlice) Type() string {
return "URLSlice"
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment