Browse Source

feat(user,vpn): implement no-gw capability

No-gw can be requested by setting the `nogw` variable to true when
creating a new user.

Closes #15.
Mustafa Arici 8 years ago
parent
commit
bf2a9b940e
8 changed files with 75 additions and 34 deletions
  1. 2 1
      api/rpc.go
  2. 6 6
      bindata/bindata.go
  3. 6 2
      cmd/ovpm/main.go
  4. 41 24
      pb/user.pb.go
  5. 2 0
      pb/user.proto
  6. 4 0
      template/client.ovpn.tmpl
  7. 12 1
      user.go
  8. 2 0
      vpn.go

+ 2 - 1
api/rpc.go

@@ -28,6 +28,7 @@ func (s *UserService) List(ctx context.Context, req *pb.UserListRequest) (*pb.Us
 			Username:           user.GetUsername(),
 			CreatedAt:          user.GetCreatedAt(),
 			IPNet:              user.GetIPNet(),
+			NoGW:               user.IsNoGW(),
 		})
 	}
 
@@ -37,7 +38,7 @@ func (s *UserService) List(ctx context.Context, req *pb.UserListRequest) (*pb.Us
 func (s *UserService) Create(ctx context.Context, req *pb.UserCreateRequest) (*pb.UserResponse, error) {
 	logrus.Debugf("rpc call: user create: %s", req.Username)
 	var ut []*pb.UserResponse_User
-	user, err := ovpm.CreateNewUser(req.Username, req.Password)
+	user, err := ovpm.CreateNewUser(req.Username, req.Password, req.NoGW)
 	if err != nil {
 		return nil, err
 	}

+ 6 - 6
bindata/bindata.go

@@ -87,12 +87,12 @@ func templateCcdFileTmpl() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "template/ccd.file.tmpl", size: 74, mode: os.FileMode(436), modTime: time.Unix(1502659670, 0)}
+	info := bindataFileInfo{name: "template/ccd.file.tmpl", size: 74, mode: os.FileMode(420), modTime: time.Unix(1502998813, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
 
-var _templateClientOvpnTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\xce\x31\x6b\x33\x31\x0c\x06\xe0\x5d\xbf\x42\xf0\x2d\x5f\x87\x8b\x87\x6e\xe5\x28\x94\x2e\x85\x52\x9a\xa9\x4b\xe9\xe0\xf3\x29\x89\x89\x6d\x19\x59\x67\x70\x83\xff\x7b\xb9\x4b\xc8\xa6\xf7\x11\x12\xef\x3f\xd4\x93\x2f\xc8\x35\x27\x3c\xf8\x40\xe8\x0b\xda\x45\x39\x5a\xf5\xce\x86\xd0\xf0\x48\x89\xc4\x2a\xcd\x38\x35\xfc\xfe\xfc\xda\x7f\xfc\xfc\x3f\xa9\xe6\xf2\x64\xcc\xd1\xeb\x69\x99\x76\x8e\xa3\x71\x76\x36\x5c\x73\x7c\x00\x70\xc1\x53\x52\x98\xa9\xa2\x2e\x09\xb2\xb0\x32\x2e\x73\x06\xa1\xc8\x4a\x78\xb9\xe0\xee\x8d\x8b\x26\x1b\x09\x7b\xdf\xf2\x9e\x45\xb1\x77\x10\x2a\x1c\xea\x20\xa4\xd2\xd0\xa7\x83\x4f\x5e\xe9\x76\x38\x38\x12\x1d\x34\x14\x2c\x24\x95\x04\x52\xb9\x51\xcb\x74\x37\x9e\x7c\x9a\x21\x93\x14\x5f\x74\x38\x53\xbb\xcf\x6b\x19\xc7\x31\x0f\xe1\x97\xa1\x92\x4c\xf8\x08\x30\x3a\xfb\x0c\x6b\x83\xd7\x17\xec\x7d\x34\x6b\x1c\xd7\xaf\x37\xa5\xad\xd7\x68\xae\x34\x9e\xa9\x5d\x17\xef\xd4\x36\xdf\xe0\x2f\x00\x00\xff\xff\xdf\x7e\x10\xe9\x46\x01\x00\x00")
+var _templateClientOvpnTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\xcf\x31\x4b\x04\x31\x10\x05\xe0\x3e\xbf\x62\xc0\x46\x8b\xbd\x2d\xec\x64\x11\xc4\x42\x41\xd4\xab\xb4\x10\x8b\x6c\xf6\xdd\x5d\xb8\x6c\x26\x4c\x66\x17\xe2\xb1\xff\x5d\xb2\x77\x5c\x97\xf7\x3d\x12\x5e\x6e\x48\x0f\x3e\x13\xcf\x29\xd2\xce\x07\x90\xcf\x64\x27\xe5\xd1\xaa\x77\x36\x84\x42\x7b\x44\x88\x55\x0c\xd4\x17\xfa\xf9\xfc\xda\xbe\xff\xde\x1e\x54\x53\x7e\x68\xdb\xbd\xd7\xc3\xd4\x6f\x1c\x8f\xad\xb3\x43\xcb\x73\x1a\xef\x8c\x71\xc1\x23\xaa\x19\x30\x93\x4e\xd1\x24\x61\x65\x9a\x86\x64\x04\x23\x2b\xe8\x74\xa2\xcd\x2b\x67\x8d\x76\x04\x2d\xcb\x9a\xb7\x2c\x4a\xcb\x62\x04\x99\xc3\xdc\x08\x54\x0a\xf9\xb8\xf3\xd1\x2b\x2e\x17\x1b\x07\xd1\x46\x43\xa6\x0c\x99\x21\x26\xe6\x0b\x95\x84\xab\x71\xef\xe3\x60\x12\x24\xfb\xac\xcd\x11\xe5\x7a\xae\x63\x1c\x8f\xa9\x09\x7f\x6c\x66\x48\x4f\xf7\xc6\x74\xce\x3e\x9a\xba\xe0\xf9\x89\x96\xa5\x6b\x6b\xec\xea\xab\x17\xc5\xba\xab\x6b\xcf\xd4\x1d\x51\xce\xc5\x1b\xca\xea\x2b\x54\xf1\x3b\xda\x7c\xf0\xcb\xf7\xfa\x0b\x9e\x14\x4d\xe4\x34\x85\x50\x3b\xc4\xa1\xf2\x7f\x00\x00\x00\xff\xff\x7e\x3b\xec\xc9\x6d\x01\x00\x00")
 
 func templateClientOvpnTmplBytes() ([]byte, error) {
 	return bindataRead(
@@ -107,7 +107,7 @@ func templateClientOvpnTmpl() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "template/client.ovpn.tmpl", size: 326, mode: os.FileMode(436), modTime: time.Unix(1502977984, 0)}
+	info := bindataFileInfo{name: "template/client.ovpn.tmpl", size: 365, mode: os.FileMode(420), modTime: time.Unix(1503000877, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
@@ -127,7 +127,7 @@ func templateDh4096PemTmpl() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "template/dh4096.pem.tmpl", size: 1468, mode: os.FileMode(436), modTime: time.Unix(1502659670, 0)}
+	info := bindataFileInfo{name: "template/dh4096.pem.tmpl", size: 1468, mode: os.FileMode(420), modTime: time.Unix(1502796579, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
@@ -147,7 +147,7 @@ func templateIptablesTmpl() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "template/iptables.tmpl", size: 0, mode: os.FileMode(436), modTime: time.Unix(1502659670, 0)}
+	info := bindataFileInfo{name: "template/iptables.tmpl", size: 0, mode: os.FileMode(420), modTime: time.Unix(1502796579, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
@@ -167,7 +167,7 @@ func templateServerConfTmpl() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "template/server.conf.tmpl", size: 9585, mode: os.FileMode(436), modTime: time.Unix(1502659670, 0)}
+	info := bindataFileInfo{name: "template/server.conf.tmpl", size: 9585, mode: os.FileMode(420), modTime: time.Unix(1502796579, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }

+ 6 - 2
cmd/ovpm/main.go

@@ -66,10 +66,10 @@ func main() {
 							return err
 						}
 						table := tablewriter.NewWriter(os.Stdout)
-						table.SetHeader([]string{"#", "username", "ip", "created at", "valid crt"})
+						table.SetHeader([]string{"#", "username", "ip", "created at", "valid crt", "no gw"})
 						//table.SetBorder(false)
 						for i, user := range resp.Users {
-							data := []string{fmt.Sprintf("%v", i+1), user.Username, user.IPNet, user.CreatedAt, fmt.Sprintf("%t", user.ServerSerialNumber == server.SerialNumber)}
+							data := []string{fmt.Sprintf("%v", i+1), user.Username, user.IPNet, user.CreatedAt, fmt.Sprintf("%t", user.ServerSerialNumber == server.SerialNumber), fmt.Sprintf("%t", user.NoGW)}
 							table.Append(data)
 						}
 						table.Render()
@@ -89,6 +89,10 @@ func main() {
 							Name:  "password, p",
 							Usage: "password for the vpn user",
 						},
+						cli.StringFlag{
+							Name:  "no-gw",
+							Usage: "don't push vpn server as default gateway for this user",
+						},
 					},
 					Action: func(c *cli.Context) error {
 						action = "user:create"

+ 41 - 24
pb/user.pb.go

@@ -54,6 +54,7 @@ func (*UserListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, [
 type UserCreateRequest struct {
 	Username string `protobuf:"bytes,1,opt,name=Username" json:"Username,omitempty"`
 	Password string `protobuf:"bytes,2,opt,name=Password" json:"Password,omitempty"`
+	NoGW     bool   `protobuf:"varint,3,opt,name=NoGW" json:"NoGW,omitempty"`
 }
 
 func (m *UserCreateRequest) Reset()                    { *m = UserCreateRequest{} }
@@ -75,6 +76,13 @@ func (m *UserCreateRequest) GetPassword() string {
 	return ""
 }
 
+func (m *UserCreateRequest) GetNoGW() bool {
+	if m != nil {
+		return m.NoGW
+	}
+	return false
+}
+
 type UserDeleteRequest struct {
 	Username string `protobuf:"bytes,1,opt,name=Username" json:"Username,omitempty"`
 }
@@ -145,6 +153,7 @@ type UserResponse_User struct {
 	Cert               string `protobuf:"bytes,3,opt,name=Cert" json:"Cert,omitempty"`
 	CreatedAt          string `protobuf:"bytes,4,opt,name=CreatedAt" json:"CreatedAt,omitempty"`
 	IPNet              string `protobuf:"bytes,5,opt,name=IPNet" json:"IPNet,omitempty"`
+	NoGW               bool   `protobuf:"varint,6,opt,name=NoGW" json:"NoGW,omitempty"`
 }
 
 func (m *UserResponse_User) Reset()                    { *m = UserResponse_User{} }
@@ -187,6 +196,13 @@ func (m *UserResponse_User) GetIPNet() string {
 	return ""
 }
 
+func (m *UserResponse_User) GetNoGW() bool {
+	if m != nil {
+		return m.NoGW
+	}
+	return false
+}
+
 type UserGenConfigResponse struct {
 	ClientConfig string `protobuf:"bytes,1,opt,name=ClientConfig" json:"ClientConfig,omitempty"`
 }
@@ -421,28 +437,29 @@ var _UserService_serviceDesc = grpc.ServiceDesc{
 func init() { proto.RegisterFile("user.proto", fileDescriptor0) }
 
 var fileDescriptor0 = []byte{
-	// 362 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xd1, 0x6a, 0xea, 0x40,
-	0x10, 0xbd, 0xd1, 0x44, 0xae, 0xa3, 0x70, 0x75, 0xae, 0xc2, 0x36, 0xf4, 0x41, 0xf6, 0x49, 0x28,
-	0x44, 0xaa, 0x8f, 0x7d, 0x6a, 0x53, 0x28, 0xa5, 0x45, 0x44, 0xe9, 0x07, 0x24, 0x75, 0x5a, 0x02,
-	0x9a, 0xa4, 0xbb, 0x6b, 0xfd, 0x97, 0xbe, 0xf4, 0xb7, 0xfa, 0x39, 0x65, 0xb3, 0x89, 0x51, 0x49,
-	0x8b, 0x6f, 0x33, 0xe7, 0xcc, 0x99, 0x4c, 0x4e, 0x4e, 0x00, 0x36, 0x92, 0x84, 0x97, 0x8a, 0x44,
-	0x25, 0x58, 0x4b, 0x43, 0xde, 0x85, 0x7f, 0x4f, 0x92, 0xc4, 0x63, 0x24, 0xd5, 0x9c, 0xde, 0x36,
-	0x24, 0x15, 0x7f, 0x80, 0xae, 0x86, 0x7c, 0x41, 0x81, 0xa2, 0x1c, 0x44, 0x17, 0xfe, 0x6a, 0x30,
-	0x0e, 0xd6, 0xc4, 0xac, 0x81, 0x35, 0x6c, 0xce, 0x77, 0xbd, 0xe6, 0x66, 0x81, 0x94, 0xdb, 0x44,
-	0x2c, 0x59, 0xcd, 0x70, 0x45, 0xcf, 0x47, 0x66, 0xd9, 0x2d, 0xad, 0xe8, 0xa4, 0x65, 0xdc, 0x83,
-	0x8e, 0xae, 0xe7, 0x14, 0xd3, 0xf6, 0x94, 0xf9, 0x31, 0xf4, 0x74, 0x7d, 0x47, 0xb1, 0x9f, 0xc4,
-	0x2f, 0xd1, 0xeb, 0x29, 0x9a, 0x2f, 0x0b, 0xda, 0xe6, 0x21, 0x32, 0x4d, 0x62, 0x49, 0x78, 0x01,
-	0x8e, 0xf6, 0x45, 0x32, 0x6b, 0x50, 0x1f, 0xb6, 0xc6, 0x7d, 0x2f, 0x0d, 0xbd, 0xfd, 0x01, 0xd3,
-	0x98, 0x19, 0xf7, 0xc3, 0x02, 0x5b, 0xf7, 0xbf, 0x7a, 0xe2, 0x01, 0x2e, 0x48, 0xbc, 0x93, 0x58,
-	0x90, 0x88, 0x82, 0xd5, 0x74, 0xb3, 0x0e, 0x49, 0xe4, 0xee, 0x54, 0x30, 0x88, 0x60, 0xfb, 0x24,
-	0x14, 0xab, 0x67, 0x13, 0x59, 0x8d, 0xe7, 0xd0, 0x34, 0x1f, 0x61, 0x79, 0xad, 0x98, 0x9d, 0x11,
-	0x25, 0x80, 0x3d, 0x70, 0xee, 0x67, 0x53, 0x52, 0xcc, 0xc9, 0x18, 0xd3, 0xf0, 0x2b, 0xe8, 0x1f,
-	0xd9, 0x91, 0xbf, 0x22, 0x87, 0xb6, 0xbf, 0x8a, 0x28, 0x56, 0x06, 0xcf, 0x0f, 0x3e, 0xc0, 0xc6,
-	0x9f, 0x35, 0x68, 0x69, 0xb5, 0xbe, 0x2f, 0x7a, 0x26, 0x1c, 0x81, 0xad, 0x83, 0x81, 0xff, 0x0b,
-	0x3f, 0xf6, 0x62, 0xe2, 0x76, 0x8e, 0x4d, 0xe2, 0x7f, 0x70, 0x02, 0x0d, 0x73, 0x20, 0xee, 0x2c,
-	0x3c, 0x88, 0xd1, 0x4f, 0x22, 0x13, 0x8f, 0x52, 0x74, 0x10, 0x97, 0x4a, 0xd1, 0x25, 0x38, 0x59,
-	0x44, 0xb0, 0x57, 0x92, 0x65, 0x62, 0x2a, 0x25, 0x37, 0xd0, 0xdc, 0xd9, 0x82, 0xac, 0x18, 0x38,
-	0x0e, 0x8e, 0x7b, 0x56, 0xc1, 0x14, 0x3b, 0xc2, 0x46, 0xf6, 0xe7, 0x4c, 0xbe, 0x03, 0x00, 0x00,
-	0xff, 0xff, 0xf5, 0x56, 0x02, 0x91, 0x47, 0x03, 0x00, 0x00,
+	// 382 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x5d, 0x6b, 0xea, 0x40,
+	0x10, 0xbd, 0xd1, 0x44, 0xcc, 0x28, 0x5c, 0x9d, 0xab, 0x90, 0x1b, 0xee, 0x83, 0xe4, 0x49, 0xb8,
+	0x10, 0xa9, 0x3e, 0xf6, 0xa9, 0x4d, 0x41, 0x0a, 0x45, 0x24, 0x52, 0xfa, 0x58, 0x92, 0x3a, 0x2d,
+	0x01, 0x4d, 0xd2, 0xdd, 0xb5, 0xfe, 0x9c, 0xfe, 0x83, 0xfe, 0xb2, 0xfe, 0x88, 0xb2, 0xd9, 0x7c,
+	0xa8, 0xa4, 0xc5, 0xb7, 0x99, 0x73, 0xe6, 0x64, 0x77, 0xcf, 0x9c, 0x00, 0xec, 0x38, 0x31, 0x37,
+	0x65, 0x89, 0x48, 0xb0, 0x91, 0x86, 0x4e, 0x1f, 0x7e, 0xdf, 0x73, 0x62, 0x77, 0x11, 0x17, 0x3e,
+	0xbd, 0xee, 0x88, 0x0b, 0xe7, 0x11, 0xfa, 0x12, 0xf2, 0x18, 0x05, 0x82, 0x72, 0x10, 0x6d, 0x68,
+	0x4b, 0x30, 0x0e, 0xb6, 0x64, 0x69, 0x23, 0x6d, 0x6c, 0xfa, 0x65, 0x2f, 0xb9, 0x65, 0xc0, 0xf9,
+	0x3e, 0x61, 0x6b, 0xab, 0xa1, 0xb8, 0xa2, 0x47, 0x04, 0x7d, 0x91, 0xcc, 0x1f, 0xac, 0xe6, 0x48,
+	0x1b, 0xb7, 0xfd, 0xac, 0x76, 0x26, 0xea, 0x80, 0x1b, 0xda, 0xd0, 0x59, 0x07, 0x38, 0x2e, 0xf4,
+	0x64, 0xed, 0x53, 0x4c, 0xfb, 0x73, 0xe6, 0xa7, 0x30, 0x90, 0xf5, 0x9c, 0x62, 0x2f, 0x89, 0x9f,
+	0xa3, 0x97, 0x73, 0x34, 0x9f, 0x1a, 0x74, 0xd5, 0x21, 0x3c, 0x4d, 0x62, 0x4e, 0xf8, 0x1f, 0x0c,
+	0xe9, 0x15, 0xb7, 0xb4, 0x51, 0x73, 0xdc, 0x99, 0x0e, 0xdd, 0x34, 0x74, 0x0f, 0x07, 0x54, 0xa3,
+	0x66, 0xec, 0x0f, 0x0d, 0x74, 0xd9, 0xff, 0xe8, 0x93, 0x0b, 0xb8, 0x22, 0xf6, 0x46, 0x6c, 0x45,
+	0x2c, 0x0a, 0x36, 0x8b, 0xdd, 0x36, 0x24, 0x96, 0x3b, 0x56, 0xc3, 0x48, 0xef, 0x3c, 0x62, 0x22,
+	0xf3, 0xce, 0xf4, 0xb3, 0x1a, 0xff, 0x81, 0xa9, 0x16, 0xb3, 0xbe, 0x12, 0x96, 0x9e, 0x11, 0x15,
+	0x80, 0x03, 0x30, 0x6e, 0x97, 0x0b, 0x12, 0x96, 0x91, 0x31, 0xaa, 0x29, 0x77, 0xd0, 0x3a, 0xd8,
+	0xc1, 0x25, 0x0c, 0x4f, 0x2c, 0xca, 0x9f, 0xed, 0x40, 0xd7, 0xdb, 0x44, 0x14, 0x0b, 0x85, 0xe7,
+	0x8f, 0x38, 0xc2, 0xa6, 0xef, 0x0d, 0xe8, 0x48, 0xb5, 0xbc, 0x73, 0xf4, 0x44, 0x38, 0x01, 0x5d,
+	0x06, 0x08, 0xff, 0x14, 0x1e, 0x1d, 0xc4, 0xc9, 0xee, 0x9d, 0x1a, 0xe7, 0xfc, 0xc2, 0x19, 0xb4,
+	0xd4, 0xa5, 0xb1, 0xb4, 0xf5, 0x28, 0x6e, 0xdf, 0x89, 0x54, 0x64, 0x2a, 0xd1, 0x51, 0x84, 0x6a,
+	0x45, 0x17, 0x60, 0x64, 0xb1, 0xc1, 0x41, 0x45, 0x56, 0x29, 0xaa, 0x95, 0x5c, 0x83, 0x59, 0xda,
+	0x82, 0x56, 0x31, 0x70, 0x1a, 0x26, 0xfb, 0x6f, 0x0d, 0x53, 0x7c, 0x23, 0x6c, 0x65, 0x7f, 0xd8,
+	0xec, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xde, 0xf2, 0x7b, 0x7e, 0x6f, 0x03, 0x00, 0x00,
 }

+ 2 - 0
pb/user.proto

@@ -9,6 +9,7 @@ message UserListRequest {
 message UserCreateRequest {
   string Username = 1;
   string Password = 2;
+  bool NoGW = 3;
 }
 
 message UserDeleteRequest {
@@ -38,6 +39,7 @@ message UserResponse {
     string Cert = 3;
     string CreatedAt = 4;
     string IPNet = 5;
+    bool NoGW = 6;
   }
 
   repeated User users = 1;

+ 4 - 0
template/client.ovpn.tmpl

@@ -19,3 +19,7 @@ verb 3
 {{ .Cert }}</cert>
 <key>
 {{ .Key }}</key>
+
+{{ if .NoGW }}
+route-nopull
+{{ end }}

+ 12 - 1
user.go

@@ -16,6 +16,8 @@ type User interface {
 	GetUsername() string
 	GetServerSerialNumber() string
 	GetCert() string
+	GetIPNet() string
+	IsNoGW() bool
 }
 
 // DBUser is database model for VPN users.
@@ -29,6 +31,7 @@ type DBUser struct {
 	ServerSerialNumber string
 	Password           string
 	Key                string
+	NoGW               bool
 }
 
 // DBRevoked is a database model for revoked VPN users.
@@ -69,9 +72,11 @@ func GetAllUsers() ([]*DBUser, error) {
 }
 
 // CreateNewUser creates a new user with the given username and password in the database.
+// If nogw is true, then ovpm doesn't push vpn server as the default gw for the user.
+//
 // It also generates the necessary client keys and signs certificates with the current
 // server's CA.
-func CreateNewUser(username, password string) (*DBUser, error) {
+func CreateNewUser(username, password string, nogw bool) (*DBUser, error) {
 	if !IsInitialized() {
 		return nil, fmt.Errorf("you first need to create server")
 	}
@@ -102,6 +107,7 @@ func CreateNewUser(username, password string) (*DBUser, error) {
 		Cert:               clientCert.Cert,
 		Key:                clientCert.Key,
 		ServerSerialNumber: server.SerialNumber,
+		NoGW:               nogw,
 	}
 
 	db.Create(&user)
@@ -235,3 +241,8 @@ func (u *DBUser) GetIPNet() string {
 	}
 	return ipn.String()
 }
+
+// IsNoGW returns wether user is set to get the vpn server as their default gateway.
+func (u *DBUser) IsNoGW() bool {
+	return u.NoGW
+}

+ 2 - 0
vpn.go

@@ -169,12 +169,14 @@ func DumpsClientConfig(username string) (string, error) {
 		CA       string
 		Key      string
 		Cert     string
+		NoGW     bool
 	}{
 		Hostname: server.Hostname,
 		Port:     server.Port,
 		CA:       server.CACert,
 		Key:      user.Key,
 		Cert:     user.Cert,
+		NoGW:     user.NoGW,
 	}
 	data, err := bindata.Asset("template/client.ovpn.tmpl")
 	if err != nil {