improve permissions system
This commit is contained in:
parent
0d918b7540
commit
e18aa583d0
34
httpd.go
34
httpd.go
|
@ -26,13 +26,13 @@ type jsonInputParams struct {
|
|||
}
|
||||
|
||||
type HttpServer struct {
|
||||
Port string
|
||||
ovpn *OpenVpnMgt
|
||||
key string
|
||||
cert string
|
||||
minProfile string
|
||||
neededProfile string
|
||||
certPool *x509.CertPool
|
||||
Port string
|
||||
ovpn *OpenVpnMgt
|
||||
key string
|
||||
cert string
|
||||
minProfile string
|
||||
neededProfiles []string
|
||||
certPool *x509.CertPool
|
||||
}
|
||||
|
||||
func parseJsonQuery(r *http.Request) (*jsonInput, error) {
|
||||
|
@ -119,9 +119,9 @@ func (h *HttpServer) ajaxHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
webuser := strings.Replace(r.TLS.PeerCertificates[0].Subject.CommonName, " ", "", -1)
|
||||
profile, _, _ := h.ovpn.AuthLoop(h.minProfile, webuser, "", false)
|
||||
if profile != h.neededProfile {
|
||||
http.Error(w, fmt.Sprintf("You need the %s profile", h.neededProfile), 403)
|
||||
_, _, _, profilePath := h.ovpn.AuthLoop(h.minProfile, webuser, "", false)
|
||||
if inArray(h.neededProfiles, profilePath) {
|
||||
http.Error(w, fmt.Sprintf("You need on of %s profile", h.neededProfiles), 403)
|
||||
return
|
||||
}
|
||||
log.Printf("%s is connected via the web interfaces\n", webuser)
|
||||
|
@ -152,14 +152,14 @@ func (h *HttpServer) ajaxHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
func NewHTTPServer(port, key, cert, ca, minProfile, neededProfile string, s *OpenVpnMgt) {
|
||||
func NewHTTPServer(port, key, cert, ca, minProfile string, neededProfiles []string, s *OpenVpnMgt) {
|
||||
h := &HttpServer{
|
||||
Port: port,
|
||||
ovpn: s,
|
||||
key: key,
|
||||
cert: cert,
|
||||
minProfile: minProfile,
|
||||
neededProfile: neededProfile,
|
||||
Port: port,
|
||||
ovpn: s,
|
||||
key: key,
|
||||
cert: cert,
|
||||
minProfile: minProfile,
|
||||
neededProfiles: neededProfiles,
|
||||
}
|
||||
|
||||
http.HandleFunc("/help", h.helpHandler)
|
||||
|
|
20
ldap.go
20
ldap.go
|
@ -25,7 +25,7 @@ type ldapConfig struct {
|
|||
certAuth string
|
||||
ipMin net.IP
|
||||
ipMax net.IP
|
||||
upgradeFrom string
|
||||
upgradeFrom []string
|
||||
routes []string
|
||||
}
|
||||
|
||||
|
@ -46,21 +46,31 @@ func (l *ldapConfig) addIPRange(s string) error {
|
|||
|
||||
// auth loop. Try all auth profiles from startProfile
|
||||
// return the last possible profile and the mail if we found a mail like login
|
||||
func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck bool) (string, string, string) {
|
||||
func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck bool) (string, string, string, []string) {
|
||||
login := []string{user}
|
||||
profile := startProfile
|
||||
mail := ""
|
||||
otpSalt := ""
|
||||
profilePath := []string{}
|
||||
|
||||
re := rcache.Get("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$")
|
||||
|
||||
for {
|
||||
// the first login that match the mail regexp is the mail address
|
||||
if mail == "" && re.MatchString(login[0]) {
|
||||
mail = login[0]
|
||||
}
|
||||
n := profile
|
||||
for k, ldap := range s.ldap {
|
||||
if ldap.upgradeFrom != profile {
|
||||
// check if the current profile is an upgrade of the previous one
|
||||
// check the startup profile first
|
||||
ok := (profile == "") && (len(ldap.upgradeFrom) == 0)
|
||||
for _, possible := range ldap.upgradeFrom {
|
||||
if possible == profile {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
err, userOk, passOk, attributes := ldap.Auth(login, pass)
|
||||
|
@ -90,6 +100,8 @@ func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck
|
|||
// we have either a positive auth ok a previous valid one
|
||||
if passOk || profile != "" || overridePwdCheck {
|
||||
profile = k
|
||||
profilePath = append(profilePath, profile)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +112,7 @@ func (s *OpenVpnMgt) AuthLoop(startProfile, user, pass string, overridePwdCheck
|
|||
}
|
||||
}
|
||||
|
||||
return profile, mail, otpSalt
|
||||
return profile, mail, otpSalt, profilePath
|
||||
}
|
||||
|
||||
// override the real DialTLS function
|
||||
|
|
4
main.go
4
main.go
|
@ -74,7 +74,7 @@ func main() {
|
|||
routes: parseConfigArray(config, profile+".routes"),
|
||||
mfaType: config.GetString(profile+".mfa", ""),
|
||||
certAuth: config.GetString(profile+".cert", "optionnal"),
|
||||
upgradeFrom: config.GetString(profile+".upgradeFrom", ""),
|
||||
upgradeFrom: parseConfigArray(config, profile+".upgradeFrom"),
|
||||
}
|
||||
if err := ldapConf.addIPRange(config.GetString(profile+".IPRange", "")); err != nil {
|
||||
log.Println(err)
|
||||
|
@ -97,6 +97,6 @@ func main() {
|
|||
config.GetString("config.http.cert", ""),
|
||||
config.GetString("config.http.ca", ""),
|
||||
config.GetString("config.http.startAuth", "CORP"),
|
||||
config.GetString("config.http.reqAuth", "ADMINS"),
|
||||
parseConfigArray(config, "config.http.reqAuth"),
|
||||
server)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,26 @@ config
|
|||
{
|
||||
profiles:
|
||||
{
|
||||
###################################################################
|
||||
### Security Model ###
|
||||
### ###
|
||||
### +---> CONTRACT ###
|
||||
### | ###
|
||||
### start-here +-------> DATACENTER ###
|
||||
### | | ###
|
||||
### +---> CORP ------------> DEV -----> ADMINS ###
|
||||
### | ^ ###
|
||||
### | | ###
|
||||
### +--> IT-AND-SEC ---+ ###
|
||||
### ###
|
||||
### CORP/IT-AND-SEC have the same IPs but not the web perms ###
|
||||
### ADMIN/DATACENTER have the same IPs but not the web perms ###
|
||||
### ###
|
||||
### attributes[0] must match validGroups ###
|
||||
### attributes[1] is the login for the next security groups ###
|
||||
### attributes[2] is used as a salt for mfa generation ###
|
||||
### ###
|
||||
###################################################################
|
||||
CONTRACT:
|
||||
{
|
||||
servers: [ "dc-11.office.daily","dc-12.office.daily","dc-13.office.daily" ]
|
||||
|
@ -9,13 +29,12 @@ config
|
|||
bindCn: "CN=VPN Service,OU=Services,OU=Dailymotion,DC=office,DC=daily",
|
||||
bindPw: "********************",
|
||||
searchFilter: "(&(sAMAccountName=%s))"
|
||||
attributes: [ "memberOf", "mail" ]
|
||||
attributes: [ "memberOf", "mail", "extensionAttribute8" ]
|
||||
validGroups:
|
||||
[
|
||||
"CN=SEC_VPN_Users_External,OU=Security,OU=Groups,OU=Dailymotion,DC=office,DC=daily",
|
||||
]
|
||||
mfa: "okta"
|
||||
cert: "ignore"
|
||||
mfa: "internal"
|
||||
IPRange: "192.168.207.1 - 192.168.207.254",
|
||||
routes:
|
||||
[
|
||||
|
@ -38,15 +57,32 @@ config
|
|||
bindCn: "CN=VPN Service,OU=Services,OU=Dailymotion,DC=office,DC=daily",
|
||||
bindPw: "********************",
|
||||
searchFilter: "(&(sAMAccountName=%s))"
|
||||
attributes: [ "memberOf", "mail" ]
|
||||
attributes: [ "memberOf", "mail", "extensionAttribute8" ]
|
||||
validGroups:
|
||||
[
|
||||
"CN=SEC_VPN,OU=Security,OU=Groups,OU=Dailymotion,DC=office,DC=daily",
|
||||
]
|
||||
mfa: "okta"
|
||||
cert: "optionnal"
|
||||
mfa: ""
|
||||
IPRange: "192.168.201.1-192.168.203.254"
|
||||
}
|
||||
IT-AND-SEC:
|
||||
{
|
||||
servers: [ "dc-11.office.daily","dc-12.office.daily","dc-13.office.daily" ]
|
||||
baseDN: "OU=Dailymotion,DC=office,DC=daily",
|
||||
bindCn: "CN=VPN Service,OU=Services,OU=Dailymotion,DC=office,DC=daily",
|
||||
bindPw: "********************",
|
||||
searchFilter: "(&(mail=%s))"
|
||||
attributes: [ "memberOf", "mail", "extensionAttribute8" ]
|
||||
upgradeFrom: [ "CORP" ]
|
||||
validGroups:
|
||||
[
|
||||
"CN=IT-Office,OU=Security,OU=Groups,OU=Dailymotion,DC=office,DC=daily",
|
||||
"CN=Security,OU=Security,OU=Groups,OU=Dailymotion,DC=office,DC=daily",
|
||||
]
|
||||
mfa: ""
|
||||
IPRange: "192.168.201.1-192.168.203.254"
|
||||
}
|
||||
|
||||
DEV:
|
||||
{
|
||||
servers: [ "ldap-auth.vip.dailymotion.com" ]
|
||||
|
@ -55,9 +91,8 @@ config
|
|||
bindPw: "**********"
|
||||
searchFilter: "(&(mail=%s))"
|
||||
attributes: [ "description", "sshPublicKey" ]
|
||||
upgradeFrom: "CORP"
|
||||
upgradeFrom: [ "CORP", "IT-AND-SEC" ]
|
||||
mfa: ""
|
||||
cert: "optionnal"
|
||||
IPRange: "192.168.204.1-192.168.206.254"
|
||||
}
|
||||
ADMINS:
|
||||
|
@ -66,11 +101,19 @@ config
|
|||
[
|
||||
"infra",
|
||||
"net",
|
||||
]
|
||||
upgradeFrom: [ "DEV" ]
|
||||
mfa: "internal"
|
||||
IPRange: "192.168.200.2-192.168.200.254"
|
||||
}
|
||||
DATACENTER:
|
||||
{
|
||||
validGroups:
|
||||
[
|
||||
"datacenter",
|
||||
]
|
||||
upgradeFrom: "DEV"
|
||||
upgradeFrom: [ "DEV" ]
|
||||
mfa: "internal"
|
||||
cert: "mandatory"
|
||||
IPRange: "192.168.200.2-192.168.200.254"
|
||||
}
|
||||
}
|
||||
|
@ -82,9 +125,8 @@ config
|
|||
key: "/etc/ssl/private/server-key.pem"
|
||||
cert: "/etc/ssl/certs/server-bundle.pem"
|
||||
startAuth: "CORP"
|
||||
reqAuth: "ADMINS"
|
||||
reqAuth: [ "ADMINS", "IP-AND-SEC" ]
|
||||
}
|
||||
|
||||
cacheDir: "/var/run/openvpn/"
|
||||
authCa: "/usr/local/share/ca-certificates/Dailymotion.crt"
|
||||
masterSecrets: [ "********************************" ]
|
||||
|
|
|
@ -266,7 +266,7 @@ func (c *vpnSession) auth(s *OpenVpnMgt) (error, int) {
|
|||
}
|
||||
|
||||
otpSalt := ""
|
||||
c.Profile, c.Mail, otpSalt = s.AuthLoop("", c.Login, c.password, tokenPasswordOk)
|
||||
c.Profile, c.Mail, otpSalt, _ = s.AuthLoop("", c.Login, c.password, tokenPasswordOk)
|
||||
|
||||
// no profile validated, we stop here
|
||||
if c.Profile == "" {
|
||||
|
|
Loading…
Reference in New Issue