Explorar o código

Merge branch 'release-v0.2.5'

Mustafa Arici %!s(int64=8) %!d(string=hai) anos
pai
achega
b86f7b201a

+ 14 - 0
CHANGELOG.md

@@ -1,5 +1,19 @@
 # Change Log
 # Change Log
 
 
+## [v0.2.5](https://github.com/cad/ovpm/tree/v0.2.5) (2017-10-13)
+[Full Changelog](https://github.com/cad/ovpm/compare/v0.2.4...v0.2.5)
+
+**Implemented enhancements:**
+
+- WARNING: INSECURE cipher with block size less than 128 bit \(64 bit\).  [\#56](https://github.com/cad/ovpm/issues/56)
+- expose vpn restart functionality over api [\#50](https://github.com/cad/ovpm/issues/50)
+
+**Fixed bugs:**
+
+- WEBUI \> NETWORKS [\#55](https://github.com/cad/ovpm/issues/55)
+- block giving vpn server's internal ip to some user as static ip [\#54](https://github.com/cad/ovpm/issues/54)
+- when starting ensure emit [\#51](https://github.com/cad/ovpm/issues/51)
+
 ## [v0.2.4](https://github.com/cad/ovpm/tree/v0.2.4) (2017-10-06)
 ## [v0.2.4](https://github.com/cad/ovpm/tree/v0.2.4) (2017-10-06)
 [Full Changelog](https://github.com/cad/ovpm/compare/v0.2.3...v0.2.4)
 [Full Changelog](https://github.com/cad/ovpm/compare/v0.2.3...v0.2.4)
 
 

+ 1 - 1
README.md

@@ -59,7 +59,7 @@ $ systemctl enable ovpmd
 
 
 **from Source (go get):**
 **from Source (go get):**
 
 
-Only dependency for ovpm is **OpenVPN>=2.3**.
+Only dependency for ovpm is **OpenVPN>=2.3.3**.
 
 
 ```bash
 ```bash
 $ go get -u github.com/cad/ovpm/...
 $ go get -u github.com/cad/ovpm/...

+ 9 - 1
api/pb/auth.pb.gw.go

@@ -78,7 +78,15 @@ func RegisterAuthServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.Se
 // RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux".
 // RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
 func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewAuthServiceClient(conn)
+	return RegisterAuthServiceHandlerClient(ctx, mux, NewAuthServiceClient(conn))
+}
+
+// RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "AuthServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AuthServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "AuthServiceClient" to call the correct interceptors.
+func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AuthServiceClient) error {
 
 
 	mux.Handle("GET", pattern_AuthService_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 	mux.Handle("GET", pattern_AuthService_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)
 		ctx, cancel := context.WithCancel(ctx)

+ 9 - 1
api/pb/network.pb.gw.go

@@ -143,7 +143,15 @@ func RegisterNetworkServiceHandlerFromEndpoint(ctx context.Context, mux *runtime
 // RegisterNetworkServiceHandler registers the http handlers for service NetworkService to "mux".
 // RegisterNetworkServiceHandler registers the http handlers for service NetworkService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterNetworkServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
 func RegisterNetworkServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewNetworkServiceClient(conn)
+	return RegisterNetworkServiceHandlerClient(ctx, mux, NewNetworkServiceClient(conn))
+}
+
+// RegisterNetworkServiceHandler registers the http handlers for service NetworkService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "NetworkServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "NetworkServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "NetworkServiceClient" to call the correct interceptors.
+func RegisterNetworkServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client NetworkServiceClient) error {
 
 
 	mux.Handle("POST", pattern_NetworkService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 	mux.Handle("POST", pattern_NetworkService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)
 		ctx, cancel := context.WithCancel(ctx)

+ 2 - 0
api/pb/user.pb.go

@@ -22,9 +22,11 @@ It has these top-level messages:
 	VPNStatusRequest
 	VPNStatusRequest
 	VPNInitRequest
 	VPNInitRequest
 	VPNUpdateRequest
 	VPNUpdateRequest
+	VPNRestartRequest
 	VPNStatusResponse
 	VPNStatusResponse
 	VPNInitResponse
 	VPNInitResponse
 	VPNUpdateResponse
 	VPNUpdateResponse
+	VPNRestartResponse
 	NetworkCreateRequest
 	NetworkCreateRequest
 	NetworkListRequest
 	NetworkListRequest
 	NetworkDeleteRequest
 	NetworkDeleteRequest

+ 9 - 1
api/pb/user.pb.gw.go

@@ -130,7 +130,15 @@ func RegisterUserServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.Se
 // RegisterUserServiceHandler registers the http handlers for service UserService to "mux".
 // RegisterUserServiceHandler registers the http handlers for service UserService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterUserServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
 func RegisterUserServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewUserServiceClient(conn)
+	return RegisterUserServiceHandlerClient(ctx, mux, NewUserServiceClient(conn))
+}
+
+// RegisterUserServiceHandler registers the http handlers for service UserService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "UserServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "UserServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "UserServiceClient" to call the correct interceptors.
+func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client UserServiceClient) error {
 
 
 	mux.Handle("GET", pattern_UserService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 	mux.Handle("GET", pattern_UserService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)
 		ctx, cancel := context.WithCancel(ctx)

+ 88 - 35
api/pb/vpn.pb.go

@@ -122,6 +122,14 @@ func (m *VPNUpdateRequest) GetDns() string {
 	return ""
 	return ""
 }
 }
 
 
+type VPNRestartRequest struct {
+}
+
+func (m *VPNRestartRequest) Reset()                    { *m = VPNRestartRequest{} }
+func (m *VPNRestartRequest) String() string            { return proto.CompactTextString(m) }
+func (*VPNRestartRequest) ProtoMessage()               {}
+func (*VPNRestartRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
+
 type VPNStatusResponse struct {
 type VPNStatusResponse struct {
 	Name         string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
 	Name         string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
 	SerialNumber string `protobuf:"bytes,2,opt,name=serial_number,json=serialNumber" json:"serial_number,omitempty"`
 	SerialNumber string `protobuf:"bytes,2,opt,name=serial_number,json=serialNumber" json:"serial_number,omitempty"`
@@ -139,7 +147,7 @@ type VPNStatusResponse struct {
 func (m *VPNStatusResponse) Reset()                    { *m = VPNStatusResponse{} }
 func (m *VPNStatusResponse) Reset()                    { *m = VPNStatusResponse{} }
 func (m *VPNStatusResponse) String() string            { return proto.CompactTextString(m) }
 func (m *VPNStatusResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNStatusResponse) ProtoMessage()               {}
 func (*VPNStatusResponse) ProtoMessage()               {}
-func (*VPNStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
+func (*VPNStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} }
 
 
 func (m *VPNStatusResponse) GetName() string {
 func (m *VPNStatusResponse) GetName() string {
 	if m != nil {
 	if m != nil {
@@ -224,7 +232,7 @@ type VPNInitResponse struct {
 func (m *VPNInitResponse) Reset()                    { *m = VPNInitResponse{} }
 func (m *VPNInitResponse) Reset()                    { *m = VPNInitResponse{} }
 func (m *VPNInitResponse) String() string            { return proto.CompactTextString(m) }
 func (m *VPNInitResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNInitResponse) ProtoMessage()               {}
 func (*VPNInitResponse) ProtoMessage()               {}
-func (*VPNInitResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} }
+func (*VPNInitResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} }
 
 
 type VPNUpdateResponse struct {
 type VPNUpdateResponse struct {
 }
 }
@@ -232,15 +240,25 @@ type VPNUpdateResponse struct {
 func (m *VPNUpdateResponse) Reset()                    { *m = VPNUpdateResponse{} }
 func (m *VPNUpdateResponse) Reset()                    { *m = VPNUpdateResponse{} }
 func (m *VPNUpdateResponse) String() string            { return proto.CompactTextString(m) }
 func (m *VPNUpdateResponse) String() string            { return proto.CompactTextString(m) }
 func (*VPNUpdateResponse) ProtoMessage()               {}
 func (*VPNUpdateResponse) ProtoMessage()               {}
-func (*VPNUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} }
+func (*VPNUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} }
+
+type VPNRestartResponse struct {
+}
+
+func (m *VPNRestartResponse) Reset()                    { *m = VPNRestartResponse{} }
+func (m *VPNRestartResponse) String() string            { return proto.CompactTextString(m) }
+func (*VPNRestartResponse) ProtoMessage()               {}
+func (*VPNRestartResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} }
 
 
 func init() {
 func init() {
 	proto.RegisterType((*VPNStatusRequest)(nil), "pb.VPNStatusRequest")
 	proto.RegisterType((*VPNStatusRequest)(nil), "pb.VPNStatusRequest")
 	proto.RegisterType((*VPNInitRequest)(nil), "pb.VPNInitRequest")
 	proto.RegisterType((*VPNInitRequest)(nil), "pb.VPNInitRequest")
 	proto.RegisterType((*VPNUpdateRequest)(nil), "pb.VPNUpdateRequest")
 	proto.RegisterType((*VPNUpdateRequest)(nil), "pb.VPNUpdateRequest")
+	proto.RegisterType((*VPNRestartRequest)(nil), "pb.VPNRestartRequest")
 	proto.RegisterType((*VPNStatusResponse)(nil), "pb.VPNStatusResponse")
 	proto.RegisterType((*VPNStatusResponse)(nil), "pb.VPNStatusResponse")
 	proto.RegisterType((*VPNInitResponse)(nil), "pb.VPNInitResponse")
 	proto.RegisterType((*VPNInitResponse)(nil), "pb.VPNInitResponse")
 	proto.RegisterType((*VPNUpdateResponse)(nil), "pb.VPNUpdateResponse")
 	proto.RegisterType((*VPNUpdateResponse)(nil), "pb.VPNUpdateResponse")
+	proto.RegisterType((*VPNRestartResponse)(nil), "pb.VPNRestartResponse")
 	proto.RegisterEnum("pb.VPNProto", VPNProto_name, VPNProto_value)
 	proto.RegisterEnum("pb.VPNProto", VPNProto_name, VPNProto_value)
 }
 }
 
 
@@ -258,6 +276,7 @@ type VPNServiceClient interface {
 	Status(ctx context.Context, in *VPNStatusRequest, opts ...grpc.CallOption) (*VPNStatusResponse, error)
 	Status(ctx context.Context, in *VPNStatusRequest, opts ...grpc.CallOption) (*VPNStatusResponse, error)
 	Init(ctx context.Context, in *VPNInitRequest, opts ...grpc.CallOption) (*VPNInitResponse, error)
 	Init(ctx context.Context, in *VPNInitRequest, opts ...grpc.CallOption) (*VPNInitResponse, error)
 	Update(ctx context.Context, in *VPNUpdateRequest, opts ...grpc.CallOption) (*VPNUpdateResponse, error)
 	Update(ctx context.Context, in *VPNUpdateRequest, opts ...grpc.CallOption) (*VPNUpdateResponse, error)
+	Restart(ctx context.Context, in *VPNRestartRequest, opts ...grpc.CallOption) (*VPNRestartResponse, error)
 }
 }
 
 
 type vPNServiceClient struct {
 type vPNServiceClient struct {
@@ -295,12 +314,22 @@ func (c *vPNServiceClient) Update(ctx context.Context, in *VPNUpdateRequest, opt
 	return out, nil
 	return out, nil
 }
 }
 
 
+func (c *vPNServiceClient) Restart(ctx context.Context, in *VPNRestartRequest, opts ...grpc.CallOption) (*VPNRestartResponse, error) {
+	out := new(VPNRestartResponse)
+	err := grpc.Invoke(ctx, "/pb.VPNService/Restart", in, out, c.cc, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // Server API for VPNService service
 // Server API for VPNService service
 
 
 type VPNServiceServer interface {
 type VPNServiceServer interface {
 	Status(context.Context, *VPNStatusRequest) (*VPNStatusResponse, error)
 	Status(context.Context, *VPNStatusRequest) (*VPNStatusResponse, error)
 	Init(context.Context, *VPNInitRequest) (*VPNInitResponse, error)
 	Init(context.Context, *VPNInitRequest) (*VPNInitResponse, error)
 	Update(context.Context, *VPNUpdateRequest) (*VPNUpdateResponse, error)
 	Update(context.Context, *VPNUpdateRequest) (*VPNUpdateResponse, error)
+	Restart(context.Context, *VPNRestartRequest) (*VPNRestartResponse, error)
 }
 }
 
 
 func RegisterVPNServiceServer(s *grpc.Server, srv VPNServiceServer) {
 func RegisterVPNServiceServer(s *grpc.Server, srv VPNServiceServer) {
@@ -361,6 +390,24 @@ func _VPNService_Update_Handler(srv interface{}, ctx context.Context, dec func(i
 	return interceptor(ctx, in, info, handler)
 	return interceptor(ctx, in, info, handler)
 }
 }
 
 
+func _VPNService_Restart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(VPNRestartRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(VPNServiceServer).Restart(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.VPNService/Restart",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(VPNServiceServer).Restart(ctx, req.(*VPNRestartRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _VPNService_serviceDesc = grpc.ServiceDesc{
 var _VPNService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "pb.VPNService",
 	ServiceName: "pb.VPNService",
 	HandlerType: (*VPNServiceServer)(nil),
 	HandlerType: (*VPNServiceServer)(nil),
@@ -377,6 +424,10 @@ var _VPNService_serviceDesc = grpc.ServiceDesc{
 			MethodName: "Update",
 			MethodName: "Update",
 			Handler:    _VPNService_Update_Handler,
 			Handler:    _VPNService_Update_Handler,
 		},
 		},
+		{
+			MethodName: "Restart",
+			Handler:    _VPNService_Restart_Handler,
+		},
 	},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "vpn.proto",
 	Metadata: "vpn.proto",
@@ -385,37 +436,39 @@ var _VPNService_serviceDesc = grpc.ServiceDesc{
 func init() { proto.RegisterFile("vpn.proto", fileDescriptor1) }
 func init() { proto.RegisterFile("vpn.proto", fileDescriptor1) }
 
 
 var fileDescriptor1 = []byte{
 var fileDescriptor1 = []byte{
-	// 502 bytes of a gzipped FileDescriptorProto
+	// 537 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6e, 0xd3, 0x40,
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6e, 0xd3, 0x40,
-	0x10, 0x86, 0xb1, 0x93, 0x3a, 0xc9, 0x50, 0x8a, 0x3b, 0x4d, 0x55, 0x13, 0xa8, 0x54, 0x99, 0x4b,
-	0x14, 0xa4, 0x44, 0x94, 0x5b, 0x2f, 0x08, 0x0a, 0x48, 0x48, 0xc8, 0x98, 0xd0, 0xe6, 0x6a, 0xad,
-	0x9d, 0x6d, 0xb1, 0x9a, 0xec, 0x2e, 0xde, 0x4d, 0x1e, 0x00, 0x1e, 0x01, 0x89, 0x03, 0xaf, 0xc5,
-	0x2b, 0xf0, 0x20, 0xc8, 0xb3, 0x4e, 0x6a, 0x23, 0x7a, 0x9b, 0xfd, 0x67, 0xf7, 0xd7, 0x3f, 0xdf,
-	0x0e, 0xf4, 0xd6, 0x4a, 0x8c, 0x55, 0x21, 0x8d, 0x44, 0x57, 0xa5, 0x83, 0x27, 0xd7, 0x52, 0x5e,
-	0x2f, 0xf8, 0x84, 0xa9, 0x7c, 0xc2, 0x84, 0x90, 0x86, 0x99, 0x5c, 0x0a, 0x6d, 0x6f, 0x84, 0x08,
-	0xfe, 0x2c, 0x8e, 0x3e, 0x1b, 0x66, 0x56, 0x7a, 0xca, 0xbf, 0xae, 0xb8, 0x36, 0xe1, 0x2f, 0x07,
-	0xf6, 0x66, 0x71, 0xf4, 0x5e, 0xe4, 0xa6, 0x92, 0x70, 0x00, 0xdd, 0x2f, 0x52, 0x1b, 0xc1, 0x96,
-	0x3c, 0x70, 0x4e, 0x9c, 0x61, 0x6f, 0xba, 0x3d, 0x23, 0x42, 0x5b, 0xc9, 0xc2, 0x04, 0x2e, 0xe9,
-	0x54, 0xe3, 0x33, 0x00, 0xf2, 0x4f, 0x54, 0xc1, 0xaf, 0x82, 0xd6, 0x89, 0x33, 0xdc, 0x3b, 0xdd,
-	0x1d, 0xab, 0x74, 0x3c, 0x8b, 0xa3, 0xb8, 0x6c, 0x4c, 0x7b, 0xd4, 0x8f, 0x0b, 0x7e, 0x85, 0x8f,
-	0xa0, 0x9b, 0xab, 0x24, 0x5d, 0xc8, 0xec, 0x26, 0x68, 0x93, 0x49, 0x27, 0x57, 0xaf, 0xcb, 0x23,
-	0xfa, 0xd0, 0x9a, 0x0b, 0x1d, 0xec, 0x90, 0x5a, 0x96, 0xe1, 0x4b, 0x0a, 0x7c, 0xa9, 0xe6, 0xcc,
-	0xf0, 0x4d, 0xba, 0xba, 0x81, 0xf3, 0x5f, 0x03, 0xf7, 0xd6, 0xe0, 0xa7, 0x0b, 0xfb, 0xb5, 0x91,
-	0xb5, 0x92, 0x42, 0xd3, 0x10, 0xb5, 0xe1, 0xa8, 0xc6, 0xa7, 0xf0, 0x40, 0xf3, 0x22, 0x67, 0x8b,
-	0x44, 0xac, 0x96, 0x29, 0x2f, 0x2a, 0x97, 0x5d, 0x2b, 0x46, 0xa4, 0x35, 0xc8, 0xb4, 0xee, 0x20,
-	0xd3, 0xae, 0x91, 0x41, 0x68, 0x67, 0xbc, 0x30, 0xd5, 0x48, 0x54, 0xe3, 0x11, 0x74, 0x32, 0x96,
-	0x90, 0xec, 0x91, 0xec, 0x65, 0xec, 0xbc, 0x6c, 0xf8, 0xd0, 0x12, 0xdc, 0x04, 0x1d, 0x9b, 0x5e,
-	0x70, 0x7a, 0xbe, 0x64, 0xfa, 0x26, 0xe8, 0xda, 0xe7, 0x65, 0x8d, 0xc7, 0x00, 0x59, 0xc1, 0x99,
-	0xe1, 0xf3, 0x84, 0x99, 0xa0, 0x47, 0x9d, 0x5e, 0xa5, 0xbc, 0x32, 0xd8, 0x87, 0x1d, 0x62, 0x1d,
-	0x00, 0x75, 0xec, 0x61, 0x03, 0xe6, 0xfe, 0x2d, 0x98, 0x7d, 0x78, 0xb8, 0xfd, 0x75, 0x4b, 0x25,
-	0x3c, 0x20, 0x54, 0x1b, 0xd8, 0x56, 0x1c, 0x0d, 0xa1, 0xbb, 0xf9, 0x45, 0x04, 0xf0, 0xa2, 0x8f,
-	0xf1, 0xf4, 0xed, 0x3b, 0xff, 0x1e, 0x76, 0xa0, 0x75, 0xf9, 0x26, 0xf6, 0x9d, 0xb2, 0xb8, 0x38,
-	0x8f, 0x7d, 0xf7, 0xf4, 0xbb, 0x0b, 0x50, 0xa2, 0xe6, 0xc5, 0x3a, 0xcf, 0x38, 0x7e, 0x02, 0xcf,
-	0x52, 0xc7, 0x7e, 0xb5, 0x0a, 0x8d, 0xbd, 0x1b, 0x1c, 0xfe, 0xa3, 0x56, 0x21, 0x06, 0xdf, 0x7e,
-	0xff, 0xf9, 0xe1, 0xf6, 0x11, 0x69, 0x85, 0xd7, 0xcf, 0x27, 0x6b, 0x25, 0x26, 0xda, 0x1a, 0x7d,
-	0x80, 0x76, 0x19, 0x18, 0xb1, 0x7a, 0x5a, 0xdb, 0xd9, 0xc1, 0x41, 0x43, 0xab, 0xcc, 0x1e, 0x93,
-	0xd9, 0x61, 0xe8, 0xd7, 0xcd, 0x72, 0x91, 0x9b, 0x33, 0x67, 0x84, 0x17, 0xe0, 0xd9, 0x59, 0xb7,
-	0x01, 0x1b, 0x7b, 0xb6, 0x0d, 0xd8, 0x04, 0x12, 0x1e, 0x93, 0xe7, 0x51, 0xd8, 0x08, 0xb8, 0xa2,
-	0x3b, 0x67, 0xce, 0x28, 0xf5, 0x08, 0xf8, 0x8b, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x57,
-	0x61, 0xfa, 0x98, 0x03, 0x00, 0x00,
+	0x10, 0x86, 0xb1, 0x93, 0x3a, 0xc9, 0x50, 0x8a, 0x3b, 0x4d, 0xa9, 0x31, 0x54, 0xaa, 0xcc, 0xa5,
+	0x2a, 0x52, 0x22, 0xca, 0x8d, 0x0b, 0x82, 0x02, 0x12, 0x12, 0x32, 0x26, 0x34, 0xb9, 0x46, 0x1b,
+	0x67, 0x5b, 0xac, 0x26, 0xeb, 0xc5, 0xbb, 0xc9, 0x03, 0xf0, 0x0a, 0x48, 0x1c, 0x78, 0x2d, 0x6e,
+	0x9c, 0x79, 0x10, 0xe4, 0xd9, 0x75, 0x6a, 0x47, 0x70, 0x9b, 0xfd, 0x67, 0xe7, 0xd3, 0xcc, 0x3f,
+	0x03, 0xbd, 0xb5, 0x14, 0x03, 0x59, 0xe4, 0x3a, 0x47, 0x57, 0xce, 0xc2, 0xc7, 0xd7, 0x79, 0x7e,
+	0xbd, 0xe0, 0x43, 0x26, 0xb3, 0x21, 0x13, 0x22, 0xd7, 0x4c, 0x67, 0xb9, 0x50, 0xe6, 0x47, 0x84,
+	0xe0, 0x4f, 0x92, 0xf8, 0xb3, 0x66, 0x7a, 0xa5, 0x46, 0xfc, 0xeb, 0x8a, 0x2b, 0x1d, 0xfd, 0x74,
+	0x60, 0x6f, 0x92, 0xc4, 0xef, 0x45, 0xa6, 0xad, 0x84, 0x21, 0x74, 0xbf, 0xe4, 0x4a, 0x0b, 0xb6,
+	0xe4, 0x81, 0x73, 0xe2, 0x9c, 0xf6, 0x46, 0x9b, 0x37, 0x22, 0xb4, 0x65, 0x5e, 0xe8, 0xc0, 0x25,
+	0x9d, 0x62, 0x7c, 0x0a, 0x40, 0xfc, 0xa9, 0x2c, 0xf8, 0x55, 0xd0, 0x3a, 0x71, 0x4e, 0xf7, 0xce,
+	0x77, 0x07, 0x72, 0x36, 0x98, 0x24, 0x71, 0x52, 0x26, 0x46, 0x3d, 0xca, 0x27, 0x05, 0xbf, 0xc2,
+	0x87, 0xd0, 0xcd, 0xe4, 0x74, 0xb6, 0xc8, 0xd3, 0x9b, 0xa0, 0x4d, 0x90, 0x4e, 0x26, 0x5f, 0x97,
+	0x4f, 0xf4, 0xa1, 0x35, 0x17, 0x2a, 0xd8, 0x21, 0xb5, 0x0c, 0xa3, 0x97, 0xd4, 0xf0, 0x58, 0xce,
+	0x99, 0xe6, 0x55, 0x77, 0x75, 0x80, 0xf3, 0x4f, 0x80, 0x7b, 0x0b, 0x38, 0x80, 0xfd, 0x49, 0x12,
+	0x8f, 0xb8, 0xd2, 0xac, 0xa8, 0xe6, 0x8b, 0x7e, 0xb8, 0xa4, 0x56, 0x3e, 0x28, 0x99, 0x0b, 0x45,
+	0x93, 0xd5, 0x26, 0xa6, 0x18, 0x9f, 0xc0, 0x3d, 0xc5, 0x8b, 0x8c, 0x2d, 0xa6, 0x62, 0xb5, 0x9c,
+	0xf1, 0xc2, 0xa2, 0x77, 0x8d, 0x18, 0x93, 0xd6, 0xb0, 0xab, 0xf5, 0x1f, 0xbb, 0xda, 0x35, 0xbb,
+	0x10, 0xda, 0x29, 0x2f, 0xb4, 0x9d, 0x93, 0x62, 0x3c, 0x82, 0x4e, 0xca, 0xa6, 0x24, 0x7b, 0x24,
+	0x7b, 0x29, 0xbb, 0x28, 0x13, 0x3e, 0xb4, 0x04, 0xd7, 0x41, 0xc7, 0x8c, 0x24, 0x38, 0x95, 0x2f,
+	0x99, 0xba, 0x09, 0xba, 0xa6, 0xbc, 0x8c, 0xf1, 0x18, 0x20, 0x2d, 0x38, 0xd3, 0x7c, 0x3e, 0x65,
+	0x3a, 0xe8, 0x51, 0xa6, 0x67, 0x95, 0x57, 0x1a, 0xfb, 0xb0, 0x43, 0x0b, 0x08, 0x80, 0x32, 0xe6,
+	0x51, 0xb9, 0x75, 0xf7, 0xd6, 0xad, 0x7d, 0xb8, 0xbf, 0x39, 0x05, 0xe3, 0x8a, 0x35, 0xb0, 0xda,
+	0x80, 0x15, 0xfb, 0x80, 0x75, 0x57, 0x8d, 0x7a, 0x76, 0x0a, 0xdd, 0x6a, 0xe1, 0x08, 0xe0, 0xc5,
+	0x1f, 0x93, 0xd1, 0xdb, 0x77, 0xfe, 0x1d, 0xec, 0x40, 0x6b, 0xfc, 0x26, 0xf1, 0x9d, 0x32, 0xb8,
+	0xbc, 0x48, 0x7c, 0xf7, 0xfc, 0xb7, 0x0b, 0x50, 0x2e, 0x80, 0x17, 0xeb, 0x2c, 0xe5, 0xf8, 0x09,
+	0x3c, 0xb3, 0x0b, 0xec, 0xdb, 0xab, 0x69, 0x9c, 0x68, 0x78, 0xb8, 0xa5, 0xda, 0x2e, 0xc2, 0x6f,
+	0xbf, 0xfe, 0x7c, 0x77, 0xfb, 0x88, 0x74, 0xed, 0xeb, 0x67, 0xc3, 0xb5, 0x14, 0x43, 0x65, 0x40,
+	0x1f, 0xa0, 0x5d, 0x8e, 0x81, 0x68, 0x4b, 0x6b, 0xe7, 0x1d, 0x1e, 0x34, 0x34, 0x0b, 0x7b, 0x44,
+	0xb0, 0xc3, 0xc8, 0xaf, 0xc3, 0x32, 0x91, 0xe9, 0x17, 0xce, 0x19, 0x5e, 0x82, 0x67, 0x1c, 0xd8,
+	0x34, 0xd8, 0x38, 0xc9, 0x4d, 0x83, 0x5b, 0x36, 0x1d, 0x13, 0xf3, 0x28, 0x6a, 0x34, 0xb8, 0xa2,
+	0x3f, 0x25, 0x75, 0x0c, 0x1d, 0x6b, 0x21, 0x56, 0x80, 0xe6, 0xa1, 0x86, 0x0f, 0xb6, 0xe5, 0xad,
+	0x66, 0x0f, 0xea, 0xe0, 0xc2, 0x7c, 0x9a, 0x79, 0xb4, 0xdd, 0xe7, 0x7f, 0x03, 0x00, 0x00, 0xff,
+	0xff, 0x04, 0xc8, 0xb8, 0x7c, 0x1a, 0x04, 0x00, 0x00,
 }
 }

+ 51 - 1
api/pb/vpn.pb.gw.go

@@ -63,6 +63,15 @@ func request_VPNService_Update_0(ctx context.Context, marshaler runtime.Marshale
 
 
 }
 }
 
 
+func request_VPNService_Restart_0(ctx context.Context, marshaler runtime.Marshaler, client VPNServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq VPNRestartRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := client.Restart(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
 // RegisterVPNServiceHandlerFromEndpoint is same as RegisterVPNServiceHandler but
 // RegisterVPNServiceHandlerFromEndpoint is same as RegisterVPNServiceHandler but
 // automatically dials to "endpoint" and closes the connection when "ctx" gets done.
 // automatically dials to "endpoint" and closes the connection when "ctx" gets done.
 func RegisterVPNServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
 func RegisterVPNServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@@ -91,7 +100,15 @@ func RegisterVPNServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.Ser
 // RegisterVPNServiceHandler registers the http handlers for service VPNService to "mux".
 // RegisterVPNServiceHandler registers the http handlers for service VPNService to "mux".
 // The handlers forward requests to the grpc endpoint over "conn".
 // The handlers forward requests to the grpc endpoint over "conn".
 func RegisterVPNServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
 func RegisterVPNServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
-	client := NewVPNServiceClient(conn)
+	return RegisterVPNServiceHandlerClient(ctx, mux, NewVPNServiceClient(conn))
+}
+
+// RegisterVPNServiceHandler registers the http handlers for service VPNService to "mux".
+// The handlers forward requests to the grpc endpoint over the given implementation of "VPNServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "VPNServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "VPNServiceClient" to call the correct interceptors.
+func RegisterVPNServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client VPNServiceClient) error {
 
 
 	mux.Handle("GET", pattern_VPNService_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 	mux.Handle("GET", pattern_VPNService_Status_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(ctx)
 		ctx, cancel := context.WithCancel(ctx)
@@ -180,6 +197,35 @@ func RegisterVPNServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn
 
 
 	})
 	})
 
 
+	mux.Handle("POST", pattern_VPNService_Restart_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(ctx)
+		defer cancel()
+		if cn, ok := w.(http.CloseNotifier); ok {
+			go func(done <-chan struct{}, closed <-chan bool) {
+				select {
+				case <-done:
+				case <-closed:
+					cancel()
+				}
+			}(ctx.Done(), cn.CloseNotify())
+		}
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_VPNService_Restart_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_VPNService_Restart_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	return nil
 	return nil
 }
 }
 
 
@@ -189,6 +235,8 @@ var (
 	pattern_VPNService_Init_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "init"}, ""))
 	pattern_VPNService_Init_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "init"}, ""))
 
 
 	pattern_VPNService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "update"}, ""))
 	pattern_VPNService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "update"}, ""))
+
+	pattern_VPNService_Restart_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "vpn", "restart"}, ""))
 )
 )
 
 
 var (
 var (
@@ -197,4 +245,6 @@ var (
 	forward_VPNService_Init_0 = runtime.ForwardResponseMessage
 	forward_VPNService_Init_0 = runtime.ForwardResponseMessage
 
 
 	forward_VPNService_Update_0 = runtime.ForwardResponseMessage
 	forward_VPNService_Update_0 = runtime.ForwardResponseMessage
+
+	forward_VPNService_Restart_0 = runtime.ForwardResponseMessage
 )
 )

+ 8 - 0
api/pb/vpn.proto

@@ -23,6 +23,7 @@ message VPNUpdateRequest {
   string ip_block = 1;
   string ip_block = 1;
   string dns = 2;
   string dns = 2;
 }
 }
+message VPNRestartRequest {}
 
 
 
 
 service VPNService {
 service VPNService {
@@ -41,6 +42,12 @@ service VPNService {
       post: "/api/v1/vpn/update"
       post: "/api/v1/vpn/update"
       body: "*"
       body: "*"
     };}
     };}
+  rpc Restart (VPNRestartRequest) returns (VPNRestartResponse) {
+    option (google.api.http) = {
+      post: "/api/v1/vpn/restart"
+      //body: "*"
+    };}
+
 
 
 }
 }
 
 
@@ -59,3 +66,4 @@ message VPNStatusResponse {
 }
 }
 message VPNInitResponse {}
 message VPNInitResponse {}
 message VPNUpdateResponse {}
 message VPNUpdateResponse {}
+message VPNRestartResponse {}

+ 15 - 0
api/rpc.go

@@ -392,6 +392,21 @@ func (s *VPNService) Update(ctx context.Context, req *pb.VPNUpdateRequest) (*pb.
 	return &pb.VPNUpdateResponse{}, nil
 	return &pb.VPNUpdateResponse{}, nil
 }
 }
 
 
+func (s *VPNService) Restart(ctx context.Context, req *pb.VPNRestartRequest) (*pb.VPNRestartResponse, error) {
+	logrus.Debugf("rpc call: vpn restart")
+	perms, err := permset.FromContext(ctx)
+	if err != nil {
+		return nil, grpc.Errorf(codes.Unauthenticated, "Can't get permset from context")
+	}
+
+	if !perms.Contains(ovpm.RestartVPNPerm) {
+		return nil, grpc.Errorf(codes.PermissionDenied, "ovpm.UpdateVPNPerm is required for this operation.")
+	}
+
+	ovpm.RestartVPNProc()
+	return &pb.VPNRestartResponse{}, nil
+}
+
 type NetworkService struct{}
 type NetworkService struct{}
 
 
 func (s *NetworkService) List(ctx context.Context, req *pb.NetworkListRequest) (*pb.NetworkListResponse, error) {
 func (s *NetworkService) List(ctx context.Context, req *pb.NetworkListRequest) (*pb.NetworkListResponse, error) {

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
bindata/bindata.go


+ 1 - 0
cmd/ovpm/main.go

@@ -55,6 +55,7 @@ func main() {
 				vpnStatusCommand,
 				vpnStatusCommand,
 				vpnInitCommand,
 				vpnInitCommand,
 				vpnUpdateCommand,
 				vpnUpdateCommand,
+				vpnRestartCommand,
 			},
 			},
 		},
 		},
 		{
 		{

+ 20 - 0
cmd/ovpm/vpn.go

@@ -220,3 +220,23 @@ var vpnUpdateCommand = cli.Command{
 		return nil
 		return nil
 	},
 	},
 }
 }
+
+var vpnRestartCommand = cli.Command{
+	Name:    "restart",
+	Usage:   "Restart VPN server.",
+	Aliases: []string{"s"},
+	Action: func(c *cli.Context) error {
+		conn := getConn(c.GlobalString("daemon-port"))
+		defer conn.Close()
+		vpnSvc := pb.NewVPNServiceClient(conn)
+
+		_, err := vpnSvc.Restart(context.Background(), &pb.VPNRestartRequest{})
+		if err != nil {
+			os.Exit(1)
+			return err
+		}
+
+		logrus.Info("ovpm server restarted")
+		return nil
+	},
+}

+ 2 - 2
const.go

@@ -2,7 +2,7 @@ package ovpm
 
 
 const (
 const (
 	// Version defines the version of ovpm.
 	// Version defines the version of ovpm.
-	Version = "0.2.4"
+	Version = "0.2.5"
 
 
 	// DefaultVPNPort is the default OpenVPN port to listen.
 	// DefaultVPNPort is the default OpenVPN port to listen.
 	DefaultVPNPort = "1197"
 	DefaultVPNPort = "1197"
@@ -22,7 +22,7 @@ const (
 	_DefaultConfigPath   = etcBasePath + "ovpm.ini"
 	_DefaultConfigPath   = etcBasePath + "ovpm.ini"
 	_DefaultDBPath       = varBasePath + "db.sqlite3"
 	_DefaultDBPath       = varBasePath + "db.sqlite3"
 	_DefaultVPNConfPath  = varBasePath + "server.conf"
 	_DefaultVPNConfPath  = varBasePath + "server.conf"
-	_DefaultVPNCCDPath   = varBasePath + "ccd/"
+	_DefaultVPNCCDPath   = varBasePath + "ccd"
 	_DefaultCertPath     = varBasePath + "server.crt"
 	_DefaultCertPath     = varBasePath + "server.crt"
 	_DefaultKeyPath      = varBasePath + "server.key"
 	_DefaultKeyPath      = varBasePath + "server.key"
 	_DefaultCACertPath   = varBasePath + "ca.crt"
 	_DefaultCACertPath   = varBasePath + "ca.crt"

+ 1 - 1
contrib/afterupgrade.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #!/bin/bash
+systemctl daemon-reload
 if [ "`systemctl is-active ovpmd`" != "active" ]
 if [ "`systemctl is-active ovpmd`" != "active" ]
 then
 then
-    systemctl daemon-reload
     systemctl restart ovpmd
     systemctl restart ovpmd
 fi
 fi

+ 1 - 1
contrib/systemd/ovpmd.service

@@ -12,7 +12,7 @@ Conflicts=shutdown.target
 [Service]
 [Service]
 TimeoutSec=5min
 TimeoutSec=5min
 PIDFile=/var/run/ovpmd.pid
 PIDFile=/var/run/ovpmd.pid
-ExecStart=/usr/sbin/ovpmd --verbose
+ExecStart=/usr/sbin/ovpmd
 
 
 [Install]
 [Install]
 WantedBy=multi-user.target
 WantedBy=multi-user.target

+ 8 - 11
net.go

@@ -100,9 +100,6 @@ func GetNetwork(name string) (*Network, error) {
 	if govalidator.IsNull(name) {
 	if govalidator.IsNull(name) {
 		return nil, fmt.Errorf("validation error: %s can not be null", name)
 		return nil, fmt.Errorf("validation error: %s can not be null", name)
 	}
 	}
-	if !govalidator.IsAlphanumeric(name) {
-		return nil, fmt.Errorf("validation error: `%s` can only contain letters and numbers", name)
-	}
 
 
 	var network dbNetworkModel
 	var network dbNetworkModel
 	db.Preload("Users").Where(&dbNetworkModel{Name: name}).First(&network)
 	db.Preload("Users").Where(&dbNetworkModel{Name: name}).First(&network)
@@ -134,8 +131,8 @@ func CreateNewNetwork(name, cidr string, nettype NetworkType, via string) (*Netw
 	if govalidator.IsNull(name) {
 	if govalidator.IsNull(name) {
 		return nil, fmt.Errorf("validation error: %s can not be null", name)
 		return nil, fmt.Errorf("validation error: %s can not be null", name)
 	}
 	}
-	if !govalidator.Matches(name, "[\\w.]+") { // allow alphanumeric + dot
-		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers and dots", name)
+	if !govalidator.Matches(name, "^([\\w\\.]+)$") { // allow alphanumeric, underscore and dot
+		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers, underscores and dots", name)
 	}
 	}
 	if !govalidator.IsCIDR(cidr) {
 	if !govalidator.IsCIDR(cidr) {
 		return nil, fmt.Errorf("validation error: `%s` must be a network in the CIDR form", cidr)
 		return nil, fmt.Errorf("validation error: `%s` must be a network in the CIDR form", cidr)
@@ -178,7 +175,7 @@ func CreateNewNetwork(name, cidr string, nettype NetworkType, via string) (*Netw
 	if db.NewRecord(&network) {
 	if db.NewRecord(&network) {
 		return nil, fmt.Errorf("can not create network in the db")
 		return nil, fmt.Errorf("can not create network in the db")
 	}
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("network defined: %s (%s)", network.Name, network.CIDR)
 	logrus.Infof("network defined: %s (%s)", network.Name, network.CIDR)
 	return &Network{dbNetworkModel: network}, nil
 	return &Network{dbNetworkModel: network}, nil
 
 
@@ -191,7 +188,7 @@ func (n *Network) Delete() error {
 	}
 	}
 
 
 	db.Unscoped().Delete(n.dbNetworkModel)
 	db.Unscoped().Delete(n.dbNetworkModel)
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("network deleted: %s", n.Name)
 	logrus.Infof("network deleted: %s", n.Name)
 	return nil
 	return nil
 }
 }
@@ -224,7 +221,7 @@ func (n *Network) Associate(username string) error {
 	if userAssoc.Error != nil {
 	if userAssoc.Error != nil {
 		return fmt.Errorf("association failed: %v", userAssoc.Error)
 		return fmt.Errorf("association failed: %v", userAssoc.Error)
 	}
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("user '%s' is associated with the network '%s'", user.GetUsername(), n.Name)
 	logrus.Infof("user '%s' is associated with the network '%s'", user.GetUsername(), n.Name)
 	return nil
 	return nil
 }
 }
@@ -258,7 +255,7 @@ func (n *Network) Dissociate(username string) error {
 	if userAssoc.Error != nil {
 	if userAssoc.Error != nil {
 		return fmt.Errorf("disassociation failed: %v", userAssoc.Error)
 		return fmt.Errorf("disassociation failed: %v", userAssoc.Error)
 	}
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("user '%s' is dissociated with the network '%s'", user.GetUsername(), n.Name)
 	logrus.Infof("user '%s' is dissociated with the network '%s'", user.GetUsername(), n.Name)
 	return nil
 	return nil
 }
 }
@@ -516,12 +513,12 @@ func enableNat() error {
 func HostID2IP(hostid uint32) net.IP {
 func HostID2IP(hostid uint32) net.IP {
 	ip := make([]byte, 4)
 	ip := make([]byte, 4)
 	binary.BigEndian.PutUint32(ip, hostid)
 	binary.BigEndian.PutUint32(ip, hostid)
-	return net.IP(ip)
+	return net.IP(ip).To4()
 }
 }
 
 
 // IP2HostID converts an IP address to a host id (32-bit unsigned integer).
 // IP2HostID converts an IP address to a host id (32-bit unsigned integer).
 func IP2HostID(ip net.IP) uint32 {
 func IP2HostID(ip net.IP) uint32 {
-	hostid := binary.BigEndian.Uint32(ip)
+	hostid := binary.BigEndian.Uint32(ip.To4())
 	return hostid
 	return hostid
 }
 }
 
 

+ 22 - 0
net_test.go

@@ -1,6 +1,7 @@
 package ovpm
 package ovpm
 
 
 import (
 import (
+	"fmt"
 	"testing"
 	"testing"
 )
 )
 
 
@@ -49,6 +50,27 @@ func TestVPNCreateNewNetwork(t *testing.T) {
 		t.Fatalf("network CIDR is expected to be '%s' but it's '%s' instead", netType, network.Type)
 		t.Fatalf("network CIDR is expected to be '%s' but it's '%s' instead", netType, network.Type)
 	}
 	}
 
 
+	// Test username validation.
+	var networknametests = []struct {
+		networkname string
+		ok          bool
+	}{
+		{"asdf1240asfd", true},
+		{"asdf.asfd", true},
+		{"asdf12.12asfd", true},
+		{"asd1f-as4fd", false},
+		{"as0df a01sfd", false},
+		{"as0df$a01sfd", false},
+		{"as0df#a01sfd", false},
+		{"a6sdf_as1fd", true},
+	}
+
+	for i, tt := range networknametests {
+		_, err := CreateNewNetwork(tt.networkname, fmt.Sprintf("192.168.%d.0/24", i), SERVERNET, "")
+		if ok := (err == nil); ok != tt.ok {
+			t.Fatalf("expcted condition failed '%s': %v", tt.networkname, err)
+		}
+	}
 }
 }
 
 
 func TestVPNDeleteNetwork(t *testing.T) {
 func TestVPNDeleteNetwork(t *testing.T) {

+ 2 - 0
perms.go

@@ -19,6 +19,7 @@ const (
 	GetVPNStatusPerm
 	GetVPNStatusPerm
 	InitVPNPerm
 	InitVPNPerm
 	UpdateVPNPerm
 	UpdateVPNPerm
+	RestartVPNPerm
 
 
 	// Network permissions
 	// Network permissions
 	ListNetworksPerm
 	ListNetworksPerm
@@ -45,6 +46,7 @@ func AdminPerms() []permset.Perm {
 		GetVPNStatusPerm,
 		GetVPNStatusPerm,
 		InitVPNPerm,
 		InitVPNPerm,
 		UpdateVPNPerm,
 		UpdateVPNPerm,
+		RestartVPNPerm,
 		ListNetworksPerm,
 		ListNetworksPerm,
 		CreateNetworkPerm,
 		CreateNetworkPerm,
 		DeleteNetworkPerm,
 		DeleteNetworkPerm,

+ 5 - 1
supervisor/supervisor.go

@@ -252,8 +252,12 @@ func (p *Process) run(state State) func() {
 			}
 			}
 
 
 			// Process started successfully.
 			// Process started successfully.
+			if p.cmd.Process == nil {
+				logrus.Debugf("p.cmd.Process was not created")
+				p.transitionTo(FAILED)
+				return
+			}
 			logrus.Debugf("process is started %s PID %d", p.executable, p.cmd.Process.Pid)
 			logrus.Debugf("process is started %s PID %d", p.executable, p.cmd.Process.Pid)
-
 			// Process Observer
 			// Process Observer
 			go func() {
 			go func() {
 				err := p.cmd.Wait()
 				err := p.cmd.Wait()

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
template/bundle.js


+ 2 - 0
template/client.ovpn.tmpl

@@ -7,7 +7,9 @@ proto {{ .Proto }}
 remote {{ .Hostname }} {{ .Port }}
 remote {{ .Hostname }} {{ .Port }}
 resolv-retry infinite
 resolv-retry infinite
 ns-cert-type server
 ns-cert-type server
+cipher AES-128-CBC
 nobind
 nobind
+keepalive 2 20
 persist-key
 persist-key
 persist-tun
 persist-tun
 comp-lzo
 comp-lzo

+ 2 - 1
template/server.conf.tmpl

@@ -212,7 +212,7 @@ client-to-client
 # Ping every 10 seconds, assume that remote
 # Ping every 10 seconds, assume that remote
 # peer is down if no ping received during
 # peer is down if no ping received during
 # a 120 second time period.
 # a 120 second time period.
-keepalive 10 120
+keepalive 2 20
 
 
 # For extra security beyond that provided
 # For extra security beyond that provided
 # by SSL/TLS, create an "HMAC firewall"
 # by SSL/TLS, create an "HMAC firewall"
@@ -233,6 +233,7 @@ keepalive 10 120
 ;cipher BF-CBC        # Blowfish (default)
 ;cipher BF-CBC        # Blowfish (default)
 ;cipher AES-128-CBC   # AES
 ;cipher AES-128-CBC   # AES
 ;cipher DES-EDE3-CBC  # Triple-DES
 ;cipher DES-EDE3-CBC  # Triple-DES
+cipher AES-128-CBC
 
 
 # Enable compression on the VPN link.
 # Enable compression on the VPN link.
 # If you enable it here, you must also
 # If you enable it here, you must also

+ 19 - 0
template/vpn.swagger.json

@@ -41,6 +41,22 @@
         ]
         ]
       }
       }
     },
     },
+    "/api/v1/vpn/restart": {
+      "post": {
+        "operationId": "Restart",
+        "responses": {
+          "200": {
+            "description": "",
+            "schema": {
+              "$ref": "#/definitions/pbVPNRestartResponse"
+            }
+          }
+        },
+        "tags": [
+          "VPNService"
+        ]
+      }
+    },
     "/api/v1/vpn/status": {
     "/api/v1/vpn/status": {
       "get": {
       "get": {
         "operationId": "Status",
         "operationId": "Status",
@@ -117,6 +133,9 @@
       ],
       ],
       "default": "NOPREF"
       "default": "NOPREF"
     },
     },
+    "pbVPNRestartResponse": {
+      "type": "object"
+    },
     "pbVPNStatusResponse": {
     "pbVPNStatusResponse": {
       "type": "object",
       "type": "object",
       "properties": {
       "properties": {

+ 22 - 8
user.go

@@ -132,8 +132,8 @@ func CreateNewUser(username, password string, nogw bool, hostid uint32, admin bo
 	if govalidator.IsNull(username) {
 	if govalidator.IsNull(username) {
 		return nil, fmt.Errorf("validation error: %s can not be null", username)
 		return nil, fmt.Errorf("validation error: %s can not be null", username)
 	}
 	}
-	if !govalidator.Matches(username, "[\\w.]+") { // allow alphanumeric + dot
-		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers and dots", username)
+	if !govalidator.Matches(username, "^([\\w\\.]+)$") { // allow alphanumeric, underscore and dot
+		return nil, fmt.Errorf("validation error: `%s` can only contain letters, numbers, underscores and dots", username)
 	}
 	}
 	if username == "root" {
 	if username == "root" {
 		return nil, fmt.Errorf("forbidden: username root is reserved and can not be used")
 		return nil, fmt.Errorf("forbidden: username root is reserved and can not be used")
@@ -167,6 +167,20 @@ func CreateNewUser(username, password string, nogw bool, hostid uint32, admin bo
 		if hostIDsContains(getStaticHostIDs(), hostid) {
 		if hostIDsContains(getStaticHostIDs(), hostid) {
 			return nil, fmt.Errorf("ip %s is already allocated", ip)
 			return nil, fmt.Errorf("ip %s is already allocated", ip)
 		}
 		}
+
+		// Check if requested ip is allocated to the VPN server itself.
+		serverNet := net.IPNet{
+			IP:   net.ParseIP(server.Net).To4(),
+			Mask: net.IPMask(net.ParseIP(server.Mask).To4()),
+		}
+
+		ip, ipnet, err := net.ParseCIDR(serverNet.String())
+		if err != nil {
+			return nil, fmt.Errorf("can not parse: %v", err)
+		}
+		if hostid == IP2HostID(ipnet.IP)+1 { // If it's VPN server's IP addr, then don't allow it.
+			return nil, fmt.Errorf("can't assign server's ip address to a user")
+		}
 	}
 	}
 	user := dbUserModel{
 	user := dbUserModel{
 		Username:           username,
 		Username:           username,
@@ -186,8 +200,8 @@ func CreateNewUser(username, password string, nogw bool, hostid uint32, admin bo
 	}
 	}
 	logrus.Infof("user created: %s", username)
 	logrus.Infof("user created: %s", username)
 
 
-	// Emit server config
-	err = Emit()
+	// EmitWithRestart server config
+	err = EmitWithRestart()
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -233,7 +247,7 @@ func (u *User) Update(password string, nogw bool, hostid uint32, admin bool) err
 	}
 	}
 	db.Save(u.dbUserModel)
 	db.Save(u.dbUserModel)
 
 
-	err := Emit()
+	err := EmitWithRestart()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -255,7 +269,7 @@ func (u *User) Delete() error {
 	})
 	})
 	db.Unscoped().Delete(u.dbUserModel)
 	db.Unscoped().Delete(u.dbUserModel)
 	logrus.Infof("user deleted: %s", u.GetUsername())
 	logrus.Infof("user deleted: %s", u.GetUsername())
-	err = Emit()
+	err = EmitWithRestart()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -271,7 +285,7 @@ func (u *User) ResetPassword(password string) error {
 		return fmt.Errorf("user password can not be updated %s: %v", u.Username, err)
 		return fmt.Errorf("user password can not be updated %s: %v", u.Username, err)
 	}
 	}
 	db.Save(u.dbUserModel)
 	db.Save(u.dbUserModel)
-	err = Emit()
+	err = EmitWithRestart()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -310,7 +324,7 @@ func (u *User) Renew() error {
 	u.ServerSerialNumber = server.SerialNumber
 	u.ServerSerialNumber = server.SerialNumber
 
 
 	db.Save(u.dbUserModel)
 	db.Save(u.dbUserModel)
-	err = Emit()
+	err = EmitWithRestart()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 20 - 0
user_test.go

@@ -78,6 +78,25 @@ func TestCreateNewUser(t *testing.T) {
 		t.Fatalf("user creation expected to err but it didn't")
 		t.Fatalf("user creation expected to err but it didn't")
 	}
 	}
 
 
+	// Test username validation.
+	var usernametests = []struct {
+		username string
+		ok       bool
+	}{
+		{"asdf1240asfd", true},
+		{"asdf.asfd", true},
+		{"asdf12.12asfd", true},
+		{"asd1f-as4fd", false},
+		{"as0df a01sfd", false},
+		{"a6sdf_as1fd", true},
+	}
+
+	for _, tt := range usernametests {
+		_, err := ovpm.CreateNewUser(tt.username, "1234", false, 0, true)
+		if ok := (err == nil); ok != tt.ok {
+			t.Fatalf("expcted condition failed '%s': %v", tt.username, err)
+		}
+	}
 }
 }
 
 
 func TestUserUpdate(t *testing.T) {
 func TestUserUpdate(t *testing.T) {
@@ -308,6 +327,7 @@ func TestUserIPAllocator(t *testing.T) {
 		{"user4", true, ovpm.IP2HostID(net.ParseIP("10.9.0.5").To4()), "10.9.0.5/24", true},
 		{"user4", true, ovpm.IP2HostID(net.ParseIP("10.9.0.5").To4()), "10.9.0.5/24", true},
 		{"user6", true, ovpm.IP2HostID(net.ParseIP("10.9.0.7").To4()), "10.9.0.7/24", true},
 		{"user6", true, ovpm.IP2HostID(net.ParseIP("10.9.0.7").To4()), "10.9.0.7/24", true},
 		{"user7", true, 0, "10.9.0.6/24", true},
 		{"user7", true, 0, "10.9.0.6/24", true},
+		{"user6", true, ovpm.IP2HostID(net.ParseIP("10.9.0.1").To4()), "10.9.0.7/24", false},
 	}
 	}
 	for _, tt := range iptests {
 	for _, tt := range iptests {
 		user, err := ovpm.CreateNewUser(tt.username, "pass", tt.gw, tt.hostid, true)
 		user, err := ovpm.CreateNewUser(tt.username, "pass", tt.gw, tt.hostid, true)

+ 14 - 4
vpn.go

@@ -267,7 +267,7 @@ func Init(hostname string, port string, proto string, ipblock string, dns string
 		user.HostID = 0
 		user.HostID = 0
 		db.Save(&user.dbUserModel)
 		db.Save(&user.dbUserModel)
 	}
 	}
-	Emit()
+	EmitWithRestart()
 	logrus.Infof("server initialized")
 	logrus.Infof("server initialized")
 	return nil
 	return nil
 }
 }
@@ -313,7 +313,7 @@ func Update(ipblock string, dns string) error {
 			db.Save(user.dbUserModel)
 			db.Save(user.dbUserModel)
 		}
 		}
 
 
-		Emit()
+		EmitWithRestart()
 		logrus.Infof("server updated")
 		logrus.Infof("server updated")
 	}
 	}
 	return nil
 	return nil
@@ -327,7 +327,7 @@ func Deinit() error {
 
 
 	db.Unscoped().Delete(&dbServerModel{})
 	db.Unscoped().Delete(&dbServerModel{})
 	db.Unscoped().Delete(&dbRevokedModel{})
 	db.Unscoped().Delete(&dbRevokedModel{})
-	Emit()
+	EmitWithRestart()
 	return nil
 	return nil
 }
 }
 
 
@@ -422,6 +422,7 @@ func StartVPNProc() {
 		logrus.Error("OpenVPN is already started")
 		logrus.Error("OpenVPN is already started")
 		return
 		return
 	}
 	}
+	Emit()
 	vpnProc.Start()
 	vpnProc.Start()
 	ensureNatEnabled()
 	ensureNatEnabled()
 }
 }
@@ -435,6 +436,7 @@ func RestartVPNProc() {
 	if vpnProc == nil {
 	if vpnProc == nil {
 		panic(fmt.Sprintf("vpnProc is not initialized!"))
 		panic(fmt.Sprintf("vpnProc is not initialized!"))
 	}
 	}
+	Emit()
 	vpnProc.Restart()
 	vpnProc.Restart()
 	ensureNatEnabled()
 	ensureNatEnabled()
 }
 }
@@ -449,7 +451,6 @@ func StopVPNProc() {
 		return
 		return
 	}
 	}
 	vpnProc.Stop()
 	vpnProc.Stop()
-
 }
 }
 
 
 // Emit generates all needed files for the OpenVPN server and dumps them to their corresponding paths defined in the config.
 // Emit generates all needed files for the OpenVPN server and dumps them to their corresponding paths defined in the config.
@@ -509,7 +510,15 @@ func Emit() error {
 	}
 	}
 
 
 	logrus.Info("configurations emitted to the filesystem")
 	logrus.Info("configurations emitted to the filesystem")
+	return nil
+}
 
 
+// EmitWithRestart restarts vpnProc after calling EmitWithRestart().
+func EmitWithRestart() error {
+	err := Emit()
+	if err != nil {
+		return err
+	}
 	if IsInitialized() {
 	if IsInitialized() {
 		for {
 		for {
 			if vpnProc.Status() == supervisor.RUNNING || vpnProc.Status() == supervisor.STOPPED {
 			if vpnProc.Status() == supervisor.RUNNING || vpnProc.Status() == supervisor.STOPPED {
@@ -522,6 +531,7 @@ func Emit() error {
 	}
 	}
 
 
 	return nil
 	return nil
+
 }
 }
 
 
 func emitToFile(path, content string, mode uint) error {
 func emitToFile(path, content string, mode uint) error {

+ 4 - 0
webui/ovpm/app/api.js

@@ -44,6 +44,10 @@ export const endpoints = {
         path: "/vpn/status",
         path: "/vpn/status",
         method: "GET",
         method: "GET",
     },
     },
+    vpnRestart: {
+        path: "/vpn/restart",
+        method: "POST",
+    },
     netDefine: {
     netDefine: {
         path: "/network/create",
         path: "/network/create",
         method: "POST",
         method: "POST",

+ 13 - 4
webui/ovpm/app/components/Dashboard/AdminDashboard/index.jsx

@@ -191,7 +191,6 @@ export default class AdminDashboard extends React.Component {
     }
     }
 
 
     handleNewUserSave(user) {
     handleNewUserSave(user) {
-        console.log("HERE", user)
         let userObj = {
         let userObj = {
             username: user.username,
             username: user.username,
             password: user.password,
             password: user.password,
@@ -199,7 +198,6 @@ export default class AdminDashboard extends React.Component {
             host_id: 0, // handle this host_id problem
             host_id: 0, // handle this host_id problem
             is_admin: user.isAdmin,
             is_admin: user.isAdmin,
         }
         }
-        console.log("USER", user)
         userObj.no_gw = !user.pushGW
         userObj.no_gw = !user.pushGW
         userObj.admin_pref = user.isAdmin ? "ADMIN" : "NOADMIN"
         userObj.admin_pref = user.isAdmin ? "ADMIN" : "NOADMIN"
         userObj.host_id = user.ipAllocationMethod === "static" ? dot2num(user.staticIP) : 0
         userObj.host_id = user.ipAllocationMethod === "static" ? dot2num(user.staticIP) : 0
@@ -292,7 +290,6 @@ export default class AdminDashboard extends React.Component {
     }
     }
 
 
     handleDefineNetworkSave(network) {
     handleDefineNetworkSave(network) {
-        console.log("NETWORK:", network)
         this.api.call("netDefine", network, true, this.handleDefineNetworkSuccess.bind(this), this.handleDefineNetworkFailure.bind(this))
         this.api.call("netDefine", network, true, this.handleDefineNetworkSuccess.bind(this), this.handleDefineNetworkFailure.bind(this))
         this.setState({modal: ""})
         this.setState({modal: ""})
     }
     }
@@ -372,7 +369,6 @@ export default class AdminDashboard extends React.Component {
     handleAssociateUserSave(username) {
     handleAssociateUserSave(username) {
         //call
         //call
         //refresh
         //refresh
-        console.log(username)
         this.api.call("netAssociate", {name: this.state.assocNetworkName, username : username}, true, this.handleAssociateUserSuccess.bind(this), this.handleAssociateUserFailure.bind(this))
         this.api.call("netAssociate", {name: this.state.assocNetworkName, username : username}, true, this.handleAssociateUserSuccess.bind(this), this.handleAssociateUserFailure.bind(this))
         this.setState({modal: ""})
         this.setState({modal: ""})
     }
     }
@@ -406,6 +402,18 @@ export default class AdminDashboard extends React.Component {
         console.log(error)
         console.log(error)
     }
     }
 
 
+    handleRestartVPNServer() {
+        this.api.call("vpnRestart", {}, true, function() {
+            this.refresh()
+        }.bind(this), function() {
+            if ('response' in error && error.response.status == 401) {
+                this.handleAuthFailure(error)
+            }
+            console.log(error)
+        }.bind(this))
+    }
+
+
     handleLogout() {
     handleLogout() {
         ClearAuthToken()
         ClearAuthToken()
         this.setState({logout: true})
         this.setState({logout: true})
@@ -541,6 +549,7 @@ export default class AdminDashboard extends React.Component {
                                     </table>
                                     </table>
                                 </Tab>
                                 </Tab>
                                 <Tab value="vpn" label="VPN">
                                 <Tab value="vpn" label="VPN">
+                                    <Button className="mui--pull-right" color="primary" onClick={this.handleRestartVPNServer.bind(this)}>Restart VPN Server</Button>
                                     <table className="mui-table mui-table--bordered mui--text-justify">
                                     <table className="mui-table mui-table--bordered mui--text-justify">
                                         <thead>
                                         <thead>
                                             <tr>
                                             <tr>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
webui/ovpm/public/bundle.js


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio