1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
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
LogoutUrl string
}
var pwman *PwmanServer
const csrf_base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._#%!&:;?+{}[]"
func main() {
var ldapServer, ldapUser, ldapPassword, pwnedFile, krb5Conf, changePwScript, csrfSecret, serverAddr, basePath, logoutUrl 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/pwned-passwords-ordered.txt", "Path to the pwned passwords list")
flag.StringVar(&krb5Conf, "krb5-config", "./krb5.conf", "Path to kerberos config file")
flag.StringVar(&changePwScript, "changepw-script", "./scripts/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.StringVar(&logoutUrl, "logout-url", "/Shibboleth.sso/Logout", "The path to use for allowing the user to log out")
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,
LogoutUrl: logoutUrl,
}
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)
}
|