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