package main import ( "fmt" "github.com/gorilla/csrf" "github.com/namsral/flag" "log" "math/rand" "net/http" "strings" "time" ) type PwmanServer struct { LdapInfo *LdapInfo PwnedDBFile string Krb5Conf string ChangePwScript string RemoteUserHeader string BasePath string } var pwman *PwmanServer const csrf_base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._#%!&:;?+{}[]" func main() { var ldapServer, ldapUser, ldapPassword, pwnedFile, krb5Conf, changePwScript, csrfSecret, serverAddr, basePath string var ldapPort int var ldapSkipSSLVerify, csrfInsecure, gennerateCsrfKey bool flag.StringVar(&ldapServer, "ldap-server", "localhost", "the ldap server address") flag.IntVar(&ldapPort, "ldap-port", 636, "the ldap server port") flag.BoolVar(&ldapSkipSSLVerify, "ldap-ssl-skip-verify", false, "Should the ssl certificate of the ldap server be verfied") flag.StringVar(&ldapUser, "ldap-user", "cn=admin,dc=nordu,dc=net", "An ldap user that can change user attributes") flag.StringVar(&ldapPassword, "ldap-password", "", "Ldap user password") flag.StringVar(&pwnedFile, "pwned", "./pwned-passwords-ordered-2.0.txt", "Path to the pwned passwords list") flag.StringVar(&krb5Conf, "krb5", "./krb5.conf", "Path to kerberos config file") flag.StringVar(&changePwScript, "changepw-script", "./create-kdc-principal.pl", "Path to the change password script") flag.StringVar(&csrfSecret, "csrf-secret", "", "Specify csrf 32 char secret") flag.StringVar(&serverAddr, "address", ":3000", "Server address to listen on") flag.StringVar(&basePath, "base-path", "", "A base path that pwman lives under e.g. /sso") flag.BoolVar(&csrfInsecure, "csrf-insecure", false, "Allow csrf cookie to be sent over http") flag.BoolVar(&gennerateCsrfKey, "gennerate-csrf", false, "Gennerate a csrf secret") flag.Parse() if gennerateCsrfKey { fmt.Println("CSRF secret:", gennerateCsrfSecret()) return } if csrfSecret == "" { csrfSecret = gennerateCsrfSecret() log.Println("WARN", "No CSRF secret specified. Using random:", csrfSecret) } ldapInfo := &LdapInfo{Server: ldapServer, Port: ldapPort, SSLSkipVerify: ldapSkipSSLVerify, User: ldapUser, Password: ldapPassword} pwman = &PwmanServer{ LdapInfo: ldapInfo, PwnedDBFile: pwnedFile, Krb5Conf: krb5Conf, ChangePwScript: changePwScript, RemoteUserHeader: "X-Remote-User", BasePath: basePath, } v := Views() mux := http.NewServeMux() mux.Handle(basePath+"/", http.StripPrefix(basePath, FlashMessage(RemoteUser(v.Index())))) mux.Handle(basePath+"/changepw/sso/", FlashMessage(RemoteUser(v.ChangePassword("SSO")))) mux.Handle(basePath+"/changepw/tacacs/", FlashMessage(RemoteUser(v.ChangePassword("TACACS")))) mux.Handle(basePath+"/changepw/eduroam/", FlashMessage(RemoteUser(v.ChangePassword("eduroam")))) mux.Handle(basePath+"/pubkeys/", FlashMessage(RemoteUser(v.ChangeSSHKeys()))) mux.Handle(basePath+"/static/", http.StripPrefix(basePath+"/static", http.FileServer(http.Dir("static")))) CSRF := csrf.Protect([]byte(csrfSecret), csrf.Secure(!csrfInsecure)) server := &http.Server{ Addr: serverAddr, Handler: CSRF(mux), ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second, } if strings.HasPrefix(serverAddr, ":") { serverAddr = "0.0.0.0" + serverAddr } log.Println("Listening on: http://" + serverAddr) log.Fatal(server.ListenAndServe()) } func gennerateCsrfSecret() string { b := make([]byte, 32) rand.Seed(time.Now().UnixNano()) for i := range b { b[i] = csrf_base[rand.Intn(len(csrf_base))] } return string(b) }