summaryrefslogtreecommitdiff
path: root/main.go
blob: d73db97da3d8e61e0e489d25bc7da8362d38f895 (plain)
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)
}