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 {
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
var requestCmd *cobra.Command = &cobra.Command{
Use: "request",
......@@ -78,8 +53,16 @@ var requestCmd *cobra.Command = &cobra.Command{
var (
err error
pkey *rsa.PrivateKey
commonName string
profile string
basefilename string
outputdir string
)
viper.BindPFlags(cmd.PersistentFlags())
//spew.Dump(viper.AllSettings())
//spew.Dump(cmd.Args)
//spew.Dump(cmdArguments)
//os.Exit(0)
// check ca name
if !viper.IsSet("ca") {
......@@ -90,10 +73,9 @@ var requestCmd *cobra.Command = &cobra.Command{
if viper.GetString("cn") == "" {
log.Fatal("Please specify a common name (»CN«)")
}
// clean up CommonName
cnCleanUp := strings.NewReplacer("Ä", "Ae", "Ö", "Oe", "Ü", "Ue", "ä", "ae", "ö", "oe", "ü", "ue", "ß", "ss")
commonName := viper.GetString("cn")
commonName = viper.GetString("cn")
commonNameCleaned := cnCleanUp.Replace(commonName)
if commonNameCleaned != viper.GetString("cn") {
log.Printf("Transcribed illegal characters in cn »%s«: »%s«", commonName, commonNameCleaned)
......@@ -102,16 +84,16 @@ var requestCmd *cobra.Command = &cobra.Command{
}
// check profile
profile := viper.GetString("profile")
profile = viper.GetString("profile")
dfnrole, exists := dfnpki.DFNPublicRoles[profile]
if !exists {
log.Fatal("Unknown profile ", profile)
}
// set default outpath
basefilename := sanitize.Name(commonName)
basefilename = sanitize.Name(commonName)
timestamp := time.Now().Format("_2006-02-01T15_04_05")
outputdir := viper.GetString("outdir")
outputdir = viper.GetString("outdir")
// create output path
if outputdir == "" {
......@@ -195,61 +177,50 @@ var requestCmd *cobra.Command = &cobra.Command{
},
SignatureAlgorithm: x509.SHA256WithRSA,
}
spew.Dump(viper.IsSet("ou"))
// OU
if viper.IsSet("ou") {
request.Subject.OrganizationalUnit = viper.GetStringSlice("ou")
}
// O
if viper.IsSet("o") {
if viper.GetString("o") != "" {
request.Subject.Organization = []string{viper.GetString("o")}
} else {
log.Fatal("Please specify Organization (»O«)")
}
// L
if viper.IsSet("locality") {
if viper.GetString("locality") != "" {
request.Subject.Locality = []string{viper.GetString("locality")}
}
// ST
if viper.IsSet("state") {
if viper.GetString("state") != "" {
request.Subject.Province = []string{viper.GetString("state")}
}
// C
if viper.IsSet("country") {
if viper.GetString("country") != "" {
request.Subject.Country = []string{viper.GetString("country")}
} else {
log.Fatal("Please specify Country (»C«)")
}
// DNS SAN
if viper.IsSet("dns") {
request.DNSNames = viper.GetStringSlice("dns")
}
// email
// email SAN
if viper.IsSet("email") {
request.EmailAddresses = viper.GetStringSlice("email")
}
// add IP SANs
var saniplist []net.IP
// IP SAN
if viper.IsSet("ip") {
for _, ipstring := range viper.GetStringSlice("ip") {
if ip := net.ParseIP(ipstring); ip != nil {
saniplist = append(saniplist, ip)
} else {
log.Fatalf("Unable to convert »%s« to ip address", ipstring)
}
if ip, ok := viper.Get("ip").([]net.IP); ok {
request.IPAddresses = ip
}
request.IPAddresses = saniplist
}
// add URL SANS
var sanurllist []*url.URL
if viper.IsSet("url") {
for _, urlstring := range viper.GetStringSlice("url") {
if uri, err := url.Parse(urlstring); err == nil {
sanurllist = append(sanurllist, uri)
} else {
log.Fatalf("Unable to parse »%s« as URL: %s", urlstring, err)
if viper.IsSet("uri") {
if urls, ok := viper.Get("uri").([]*url.URL); ok {
request.URIs = urls
}
}
request.URIs = sanurllist
}
spew.Dump(request)
os.Exit(4)
// generate certificate request
csr, err := dfnpki.GenerateRequest(pkey, request)
......@@ -265,7 +236,6 @@ var requestCmd *cobra.Command = &cobra.Command{
}
// generate compatible argument for SAN addition via API
// TODO: support all types
var SubjectAlternativeNames []string
for _, sanGenerator := range []sanGenerator{
{
......@@ -277,18 +247,20 @@ var requestCmd *cobra.Command = &cobra.Command{
Values: viper.GetStringSlice("dns"),
},
{
// TODO: broken
Kind: dfnpki.URI,
Values: viper.GetStringSlice("uri"),
Values: strings.Split(viper.GetString("uri"), ","),
},
{
Kind: dfnpki.IP,
Values: viper.GetStringSlice("ip"),
Values: strings.Split(viper.GetString("ip"), ","),
},
} {
for _, value := range sanGenerator.Values {
SubjectAlternativeNames = append(SubjectAlternativeNames, dfnpki.SAN(sanGenerator.Kind, value))
}
}
spew.Dump(SubjectAlternativeNames)
// fix requester date for profile User
if profile == "User" {
if viper.GetString("RequesterName") == "" {
......@@ -360,32 +332,27 @@ func init() {
// Here you will define your flags and configuration settings.
requestCmd.PersistentFlags().SortFlags = false
requestCmd.PersistentFlags().StringVar(&requestArgs.Profile, "profile", "Web Server", "Certificate profile")
requestCmd.PersistentFlags().StringVarP(&requestArgs.CommonName, "cn", "c", "", "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().StringVar(&requestArgs.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(&requestArgs.State, "state", "", "Set »ST« (state) part of distinguished name")
requestCmd.PersistentFlags().StringVar(&requestArgs.Country, "country", "", "Set »C« (country) part of distinguished name")
requestCmd.PersistentFlags().StringSliceVar(&requestArgs.SANDNS, "dns", nil, "Add »DNS« (hostname or domain name) Subject Alternative Name(s)")
requestCmd.PersistentFlags().IPSliceVar(&requestArgs.SANIP, "ip", nil, "Add »IP« (ip address) Subject Alternative Name(s)")
requestCmd.PersistentFlags().StringSliceVar(&requestArgs.SANURI, "uri", nil, "Add »URI« Subject Alternative Name(s)")
requestCmd.PersistentFlags().StringSliceVar(&requestArgs.SANEmail, "email", nil, "Add »email« Subject Alternative Name(s)")
requestCmd.PersistentFlags().StringVar(&requestArgs.RequesterName, "RequesterName", "", "Name of requester (»Beantrager«); set to CommonName for personal certificate")
requestCmd.PersistentFlags().StringVar(&requestArgs.RequesterEmail, "RequesterEmail", "", "E-Mail of requester (»Beantrager«)")
requestCmd.PersistentFlags().StringVar(&requestArgs.RequesterOU, "RequesterOU", "", "Organisational Unit of requester (»Beantrager«)")
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.PersistentFlags().StringVar(&cmdArguments.Profile, "profile", "Web Server", "Certificate profile")
requestCmd.PersistentFlags().StringVar(&cmdArguments.CommonName, "cn", "", "Set »CommonName« (common name) part of distinguished name")
requestCmd.PersistentFlags().StringSliceVar(&cmdArguments.OU, "ou", nil, "Set »OU« (organizational unit) part(s) of distinguished name")
requestCmd.PersistentFlags().StringVar(&cmdArguments.Organisation, "o", "", "Set »O« (organization) part of distinguished name")
requestCmd.PersistentFlags().StringVar(&cmdArguments.Locality, "locality", "", "Set »L« (locality) part of distinguished name")
requestCmd.PersistentFlags().StringVar(&cmdArguments.State, "state", "", "Set »ST« (state) 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().Var(&cmdArguments.IP, "ip", "Add »IP« (ip address) Subject Alternative Name(s)")
requestCmd.PersistentFlags().Var(&cmdArguments.URI, "uri", "Add »URI« Subject Alternative Name(s)")
requestCmd.PersistentFlags().StringSliceVar(&cmdArguments.Email, "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(&cmdArguments.RequesterOU, "RequesterOU", "", "Organisational Unit of requester (»Beantrager«)")
requestCmd.PersistentFlags().BoolVar(&cmdArguments.Publish, "publish", true, "Publish certificate")
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.MarkFlagFilename("keyfile", "")
requestCmd.PersistentFlags().StringVarP(&requestArgs.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().StringVarP(&requestArgs.Outdir, "outdir", "o", "", "Directory for all output files")
requestCmd.PersistentFlags().BoolVarP(&requestArgs.DryRun, "dry-run", "n", false, "Only show request data, don't execute anything")
requestCmd.PersistentFlags().BoolVarP(&requestArgs.SkipPDF, "skip-pdf", "s", false, "Don't fetch pdf after request")
requestCmd.PersistentFlags().StringVarP(&cmdArguments.Keypass, "keypass", "p", "", "Password if secret key is encrypted")
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(&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(&cmdArguments.SkipPDF, "skip-pdf", "s", false, "Don't fetch pdf after request")
}
......@@ -56,11 +56,35 @@ var (
// has an action associated with it:
//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
globalArgs struct {
cmdArguments struct {
CAName string
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() {
rootCmd.Flags().SortFlags = false
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().IntVarP(&globalArgs.RAId, "ra-id", "R", 0, "Sub-RA identifier (usually 0 for main RA)")
viper.SetEnvPrefix("DFNPKI")
rootCmd.PersistentFlags().StringVarP(&cmdArguments.CAName, "ca", "C", "", "Certification Authority identifier")
rootCmd.PersistentFlags().IntVarP(&cmdArguments.RAId, "ra-id", "R", 0, "Sub-RA identifier (usually 0 for main RA)")
viper.BindPFlag("ca", rootCmd.PersistentFlags().Lookup("ca"))
viper.BindPFlag("raid", rootCmd.PersistentFlags().Lookup("ra-id"))
//viper.BindPFlag("ca", rootCmd.PersistentFlags().Lookup("ca"))
//viper.BindPFlag("raid", rootCmd.PersistentFlags().Lookup("ra-id"))
viper.BindPFlags(rootCmd.PersistentFlags())
}
// 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