author | Alberto Bertogli
<albertito@blitiri.com.ar> 2017-03-14 00:01:27 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2017-03-14 00:01:27 UTC |
parent | aae3b7b441c00e9acffc3e462ebf0a845754cf5a |
kxc/kxc.go | +11 | -11 |
kxd/email.go | +7 | -3 |
kxd/key_config.go | +17 | -7 |
kxd/kxd.go | +25 | -21 |
diff --git a/kxc/kxc.go b/kxc/kxc.go index 5d5e02b..955cc3e 100644 --- a/kxc/kxc.go +++ b/kxc/kxc.go @@ -20,15 +20,15 @@ import ( const defaultPort = 19840 -var server_cert = flag.String( +var serverCert = flag.String( "server_cert", "", "File containing valid server certificate(s)") -var client_cert = flag.String( +var clientCert = flag.String( "client_cert", "", "File containing the client certificate") -var client_key = flag.String( +var clientKey = flag.String( "client_key", "", "File containing the client private key") -func LoadServerCerts() (*x509.CertPool, error) { - pemData, err := ioutil.ReadFile(*server_cert) +func loadServerCerts() (*x509.CertPool, error) { + pemData, err := ioutil.ReadFile(*serverCert) if err != nil { return nil, err } @@ -48,7 +48,7 @@ func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } -func ExtractURL(rawurl string) (*url.URL, error) { +func extractURL(rawurl string) (*url.URL, error) { serverURL, err := url.Parse(rawurl) if err != nil { return nil, err @@ -78,19 +78,19 @@ func ExtractURL(rawurl string) (*url.URL, error) { return serverURL, nil } -func MakeTLSConf() *tls.Config { +func makeTLSConf() *tls.Config { var err error tlsConf := &tls.Config{} tlsConf.Certificates = make([]tls.Certificate, 1) tlsConf.Certificates[0], err = tls.LoadX509KeyPair( - *client_cert, *client_key) + *clientCert, *clientKey) if err != nil { log.Fatalf("Failed to load keys: %s", err) } // Compare against the server certificates. - serverCerts, err := LoadServerCerts() + serverCerts, err := loadServerCerts() if err != nil { log.Fatalf("Failed to load server certs: %s", err) } @@ -104,14 +104,14 @@ func main() { flag.Parse() tr := &http.Transport{ - TLSClientConfig: MakeTLSConf(), + TLSClientConfig: makeTLSConf(), } client := &http.Client{ Transport: tr, } - serverURL, err := ExtractURL(flag.Arg(0)) + serverURL, err := extractURL(flag.Arg(0)) if err != nil { log.Fatalf("Failed to extract the URL: %s", err) } diff --git a/kxd/email.go b/kxd/email.go index ea42121..64de4ef 100644 --- a/kxd/email.go +++ b/kxd/email.go @@ -11,6 +11,7 @@ import ( "time" ) +// EmailBody represents the body of an email message to sent. type EmailBody struct { From string To string @@ -52,6 +53,8 @@ func init() { template.Must(emailTmpl.Parse(emailTmplBody)) } +// NameToString converts a pkix.Name from a certificate to a human-friendly +// string. func NameToString(name pkix.Name) string { s := make([]string, 0) for _, c := range name.Country { @@ -71,9 +74,10 @@ func NameToString(name pkix.Name) string { return strings.Join(s, " ") } +// SendMail sends an email notifying of an access to the given key. func SendMail(kc *KeyConfig, req *Request, chains [][]*x509.Certificate) error { - if *smtp_addr == "" { + if *smtpAddr == "" { req.Printf("Skipping notifications") return nil } @@ -94,7 +98,7 @@ func SendMail(kc *KeyConfig, req *Request, now := time.Now() body := EmailBody{ - From: *email_from, + From: *emailFrom, To: strings.Join(emailTo, ", "), Key: keyPath, Time: now, @@ -111,6 +115,6 @@ func SendMail(kc *KeyConfig, req *Request, return err } - return smtp.SendMail(*smtp_addr, nil, *email_from, emailTo, + return smtp.SendMail(*smtpAddr, nil, *emailFrom, emailTo, msg.Bytes()) } diff --git a/kxd/key_config.go b/kxd/key_config.go index b4b191d..48926fd 100644 --- a/kxd/key_config.go +++ b/kxd/key_config.go @@ -9,7 +9,7 @@ import ( "strings" ) -func FileStat(path string) (os.FileInfo, error) { +func fileStat(path string) (os.FileInfo, error) { fd, err := os.Open(path) if err != nil { return nil, err @@ -18,8 +18,8 @@ func FileStat(path string) (os.FileInfo, error) { return fd.Stat() } -func IsDir(path string) (bool, error) { - fi, err := FileStat(path) +func isDir(path string) (bool, error) { + fi, err := fileStat(path) if err != nil { return false, err } @@ -27,8 +27,8 @@ func IsDir(path string) (bool, error) { return fi.IsDir(), nil } -func IsRegular(path string) (bool, error) { - fi, err := FileStat(path) +func isRegular(path string) (bool, error) { + fi, err := fileStat(path) if err != nil { return false, err } @@ -54,6 +54,8 @@ type KeyConfig struct { allowedHosts []string } +// NewKeyConfig makes a new KeyConfig based on the given path. Note that there +// is no check about the key existing or being valid. func NewKeyConfig(configPath string) *KeyConfig { return &KeyConfig{ ConfigPath: configPath, @@ -65,8 +67,9 @@ func NewKeyConfig(configPath string) *KeyConfig { } } +// Exists checks if this key exists. func (kc *KeyConfig) Exists() (bool, error) { - isDir, err := IsDir(kc.ConfigPath) + isDir, err := isDir(kc.ConfigPath) if os.IsNotExist(err) { return false, nil } else if err != nil { @@ -76,7 +79,7 @@ func (kc *KeyConfig) Exists() (bool, error) { return false, nil } - isRegular, err := IsRegular(kc.keyPath) + isRegular, err := isRegular(kc.keyPath) if os.IsNotExist(err) { return false, nil } else if err != nil { @@ -86,6 +89,7 @@ func (kc *KeyConfig) Exists() (bool, error) { return isRegular, nil } +// LoadClientCerts loads the client certificates allowed for this key. func (kc *KeyConfig) LoadClientCerts() error { rawContents, err := ioutil.ReadFile(kc.allowedClientsPath) if os.IsNotExist(err) { @@ -101,6 +105,7 @@ func (kc *KeyConfig) LoadClientCerts() error { return nil } +// LoadAllowedHosts loads the hosts allowed for this key. func (kc *KeyConfig) LoadAllowedHosts() error { contents, err := ioutil.ReadFile(kc.allowedHostsPath) if os.IsNotExist(err) { @@ -133,6 +138,8 @@ func (kc *KeyConfig) LoadAllowedHosts() error { return nil } +// IsAnyCertAllowed checks if any of the given certificates is allowed to +// access this key. If so, it returns the chain for each of them. func (kc *KeyConfig) IsAnyCertAllowed( certs []*x509.Certificate) [][]*x509.Certificate { opts := x509.VerifyOptions{ @@ -147,6 +154,7 @@ func (kc *KeyConfig) IsAnyCertAllowed( return nil } +// IsHostAllowed checks if the given host is allowed to access this key. func (kc *KeyConfig) IsHostAllowed(addr string) error { if kc.allowedHosts == nil { return nil @@ -166,10 +174,12 @@ func (kc *KeyConfig) IsHostAllowed(addr string) error { return fmt.Errorf("Host %q not allowed", host) } +// Key returns the private key. func (kc *KeyConfig) Key() (key []byte, err error) { return ioutil.ReadFile(kc.keyPath) } +// EmailTo returns the list of addresses to email when this key is accessed. func (kc *KeyConfig) EmailTo() ([]string, error) { contents, err := ioutil.ReadFile(kc.emailToPath) if os.IsNotExist(err) { diff --git a/kxd/kxd.go b/kxd/kxd.go index db235a9..e8e24bc 100644 --- a/kxd/kxd.go +++ b/kxd/kxd.go @@ -24,19 +24,19 @@ import ( var port = flag.Int( "port", 19840, "Port to listen on") -var ip_addr = flag.String( +var ipAddr = flag.String( "ip_addr", "", "IP address to listen on") -var data_dir = flag.String( +var dataDir = flag.String( "data_dir", "/etc/kxd/data", "Data directory") -var certfile = flag.String( +var certFile = flag.String( "cert", "/etc/kxd/cert.pem", "Certificate") -var keyfile = flag.String( +var keyFile = flag.String( "key", "/etc/kxd/key.pem", "Private key") -var smtp_addr = flag.String( +var smtpAddr = flag.String( "smtp_addr", "", "Address of the SMTP server to use to send emails") -var email_from = flag.String( +var emailFrom = flag.String( "email_from", "", "Email address to send email from") -var logfile = flag.String( +var logFile = flag.String( "logfile", "", "File to write logs to, use '-' for stdout") // Logger we will use to log entries. @@ -48,6 +48,8 @@ type Request struct { *http.Request } +// Printf is a wrapper for fmt.Printf+logging.Output, which prefixes a string +// identifying this request. func (req *Request) Printf(format string, a ...interface{}) { msg := fmt.Sprintf(format, a...) msg = fmt.Sprintf("%s %s %s", req.RemoteAddr, req.URL.Path, msg) @@ -66,16 +68,18 @@ func (req *Request) KeyPath() (string, error) { return strings.Join(s[2:], "/"), nil } -func CertToString(cert *x509.Certificate) string { +func certToString(cert *x509.Certificate) string { return fmt.Sprintf( "(0x%.8s ou:%s)", fmt.Sprintf("%x", cert.Signature), cert.Subject.OrganizationalUnit) } +// ChainToString makes a human-readable string out of the given certificate +// chain. func ChainToString(chain []*x509.Certificate) (s string) { for i, cert := range chain { - s += CertToString(cert) + s += certToString(cert) if i < len(chain)-1 { s += " -> " } @@ -110,7 +114,7 @@ func HandlerV1(w http.ResponseWriter, httpreq *http.Request) { return } - realKeyPath := path.Clean(*data_dir + "/" + keyPath) + realKeyPath := path.Clean(*dataDir + "/" + keyPath) keyConf := NewKeyConfig(realKeyPath) exists, err := keyConf.Exists() @@ -163,7 +167,7 @@ func HandlerV1(w http.ResponseWriter, httpreq *http.Request) { return } - req.Printf("Allowing request to %s", CertToString(validChains[0][0])) + req.Printf("Allowing request to %s", certToString(validChains[0][0])) err = SendMail(keyConf, &req, validChains) if err != nil { @@ -181,14 +185,14 @@ func initLog() { var err error var logfd io.Writer - if *logfile == "-" { + if *logFile == "-" { logfd = os.Stdout - } else if *logfile != "" { - logfd, err = os.OpenFile(*logfile, + } else if *logFile != "" { + logfd, err = os.OpenFile(*logFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) if err != nil { log.Fatalf("Error opening log file %s: %s", - *logfile, err) + *logFile, err) } } else { logfd, err = syslog.New( @@ -207,19 +211,19 @@ func main() { initLog() - if *smtp_addr == "" { + if *smtpAddr == "" { logging.Print( "WARNING: No emails will be sent, use --smtp_addr") } - if *email_from == "" { + if *emailFrom == "" { // Try to get a sane default if not provided, using // kxd@<smtp host>. - *email_from = fmt.Sprintf("kxd@%s", - strings.Split(*smtp_addr, ":")[0]) + *emailFrom = fmt.Sprintf("kxd@%s", + strings.Split(*smtpAddr, ":")[0]) } - listenAddr := fmt.Sprintf("%s:%d", *ip_addr, *port) + listenAddr := fmt.Sprintf("%s:%d", *ipAddr, *port) tlsConfig := tls.Config{ ClientAuth: tls.RequireAnyClientCert, @@ -234,7 +238,7 @@ func main() { http.HandleFunc("/v1/", HandlerV1) logging.Printf("Listening on %s", listenAddr) - err := server.ListenAndServeTLS(*certfile, *keyfile) + err := server.ListenAndServeTLS(*certFile, *keyFile) if err != nil { logging.Fatal(err) }