Documentation ¶
Overview ¶
The client library for registering paths with ask.systems/daemon/portal.
This package contains the raw gRPC service protos in addition to helper functions for using the service. The helpers are:
- StartTLSRegistration, MustStartTLSRegistration, StartRegistration, and MustStartRegistration are all in one helpers for getting a lease from portal and keeping it renewed in the background. Most clients should use one of these.
- ParsePattern which splits out the hostname part of multi-hostname patterns used to register with portal when portal is hosting multiple URLs. This is needed to extract the path part that can be used with [http.Handle].
- Client and the methods under it provide full access to the gRPC server using the token authentication. Call methods directly using Client.RPC if you want detailed access to portal.
You need to set the Address and Token vars to use the 4 helper functions like StartTLSRegistration so it can connect to portal. The simplest way to do this is to import the ask.systems/daemon/portal/flags library:
import ( _ "ask.systems/daemon/portal/flags" )
When running portal client binaries on the commandline you can use the PORTAL_ADDR and PORTAL_TOKEN environment variables to easily set the values.
Example ¶
package main import ( "flag" "log" "net/http" _ "ask.systems/daemon/portal/flags" // -portal_addr and -portal_token _ "ask.systems/daemon/tools/flags" // for the -version and -syslog flags "ask.systems/daemon/portal/gate" "ask.systems/daemon/tools" ) var pattern = flag.String("pattern", "/hello/", "The path to register with portal") func main() { flag.Parse() quit := make(chan struct{}) tools.CloseOnQuitSignals(quit) // close the channel when the OS says to stop // Remove the optional URL prefix from the pattern, portal can serve multiple URLs _, path := gate.ParsePattern(*pattern) // Register with portal, generate a TLS cert signed by portal, and keep the // registration and cert renewed in the background (until quit is closed) lease, tlsConf := gate.MustStartTLSRegistration(&gate.RegisterRequest{ Pattern: *pattern, }, quit) // Serve Hello World with standard go server functions http.Handle(path, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { w.Write([]byte("Hello World!")) // portal adds this header to tell you who sent the request to portal log.Printf("Hello from %v:%v", req.Header.Get("X-Forwarded-For"), req.Header.Get("X-Forwarded-For-Port")) })) // Run the server and block until the channel is closed and the graceful stop // is done. Uses the handlers registered with the http package global system tools.RunHTTPServerTLS(lease.Port, tlsConf, quit) log.Print("Goodbye") }
Output:
Index ¶
- Variables
- func ParsePattern(pattern string) (host, path string)
- func RegisterPortalServer(s grpc.ServiceRegistrar, srv PortalServer)
- func ResolveFlags() error
- type Client
- type Hostname
- type Lease
- func MustStartRegistration(request *RegisterRequest, quit <-chan struct{}) *Lease
- func MustStartTLSRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, *tls.Config)
- func StartRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, error)
- func StartTLSRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, *tls.Config, error)
- func (*Lease) Descriptor() ([]byte, []int)deprecated
- func (x *Lease) GetAddress() string
- func (x *Lease) GetCertificate() []byte
- func (x *Lease) GetPattern() string
- func (x *Lease) GetPort() uint32
- func (x *Lease) GetTimeout() *timestamppb.Timestamp
- func (*Lease) ProtoMessage()
- func (x *Lease) ProtoReflect() protoreflect.Message
- func (x *Lease) Reset()
- func (x *Lease) String() string
- type PortalClient
- type PortalServer
- type RegisterRequest
- func (*RegisterRequest) Descriptor() ([]byte, []int)deprecated
- func (x *RegisterRequest) GetAllowHttp() bool
- func (x *RegisterRequest) GetCertificateRequest() []byte
- func (x *RegisterRequest) GetFixedPort() uint32
- func (x *RegisterRequest) GetHostname() string
- func (x *RegisterRequest) GetPattern() string
- func (x *RegisterRequest) GetStripPattern() bool
- func (*RegisterRequest) ProtoMessage()
- func (x *RegisterRequest) ProtoReflect() protoreflect.Message
- func (x *RegisterRequest) Reset()
- func (x *RegisterRequest) String() string
- type UnimplementedPortalServer
- func (UnimplementedPortalServer) MyHostname(context.Context, *emptypb.Empty) (*Hostname, error)
- func (UnimplementedPortalServer) Register(context.Context, *RegisterRequest) (*Lease, error)
- func (UnimplementedPortalServer) Renew(context.Context, *Lease) (*Lease, error)
- func (UnimplementedPortalServer) Unregister(context.Context, *Lease) (*Lease, error)
- type UnsafePortalServer
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // The hostname (or IP) and port of the portal server to connect to. Address *string // The API authentication token for portal RPCs. Portal logs this on startup. Token *string )
Configuration for connecting to portal
If you read these vars directly, call ResolveFlags first!
These are set by the ask.systems/daemon/portal/flags library. If you don't want to use the flags you can set the values here.
var File_gate_service_proto protoreflect.FileDescriptor
var (
NotRegisteredError = errors.New("pattern not registered")
)
var Portal_ServiceDesc = grpc.ServiceDesc{ ServiceName: "Portal", HandlerType: (*PortalServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Register", Handler: _Portal_Register_Handler, }, { MethodName: "Renew", Handler: _Portal_Renew_Handler, }, { MethodName: "Unregister", Handler: _Portal_Unregister_Handler, }, { MethodName: "MyHostname", Handler: _Portal_MyHostname_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "gate/service.proto", }
Portal_ServiceDesc is the grpc.ServiceDesc for Portal service. It's only intended for direct use with grpc.RegisterService, and not to be introspected or modified (even as a copy)
var ServiceProto string
The text of the service.proto file so that clients can print the schema.
Functions ¶
func ParsePattern ¶
Parse a pattern in the syntax accepted by portal separating the hostname (URL) part of the pattern from the path part. The path part is then compatible with net/http.Handle
This is needed to host multiple URLs with portal.
func RegisterPortalServer ¶
func RegisterPortalServer(s grpc.ServiceRegistrar, srv PortalServer)
func ResolveFlags ¶
func ResolveFlags() error
Sets up Address and Token based on optional ask.systems/daemon/portal/flags values and with fallback to using the PORTAL_ADDR and PORTAL_TOKEN environment variable values if the the variables were not already set in code or by the flags.
Example ¶
package main import ( "log" "ask.systems/daemon/portal/gate" ) func main() { if err := gate.ResolveFlags(); err != nil { log.Fatal(err) } c, err := gate.Connect(*gate.Address, *gate.Token) if err != nil { log.Fatal(err) } defer c.Close() log.Print("Connected to portal!") }
Output:
Types ¶
type Client ¶
type Client struct { // Call any of the service.proto functions here RPC PortalClient // contains filtered or unexported fields }
Example ¶
package main import ( "context" "log" "ask.systems/daemon/portal/gate" "google.golang.org/protobuf/types/known/emptypb" ) func main() { // This call sets the globals from flags and environment variables. // import _ "ask.systems/daemon/portal/flags" to get the flag definitions. if err := gate.ResolveFlags(); err != nil { log.Fatal(err) } client, err := gate.Connect(*gate.Address, *gate.Token) if err != nil { log.Fatal(err) } defer client.Close() // Call the MyHostname RPC directly. This is used by [StartTLSRegistration] to // set the hostname in the TLS certificate request. resp, err := client.RPC.MyHostname(context.Background(), &emptypb.Empty{}) if err != nil { log.Fatal(err) } log.Print(resp.Hostname) }
Output:
func Connect ¶
Connect to the portal RPC server and don't do anything else. Use this if you want to call the proto RPCs directly.
func (Client) KeepLeaseRenewed
deprecated
Run a loop to call the Renew RPC for the given lease before the lease expires until the quit channel is closed. Intended to be run in a goroutine.
When the quit channel is closed this function unregisters the lease and closes the client.
Deprecated: It's best to use StartRegistration because this function cannot re-register the lease in the unlikely event that the server somehow forgets the lease exists when it tries to renew, because it does not have access to the registration request. In that case this function gets stuck trying to renew forever. The server can't do it because it has forgetten in this scenario.
func (Client) KeepLeaseRenewedTLS
deprecated
Run a loop to call the Renew RPC for the given lease before the lease expires until the quit channel is closed. Intended to be run in a goroutine.
Additionally, if the lease contains a TLS certificate request, the certificate is renewed with the lease. Each time the certificate is renewed, the newCert function is called with the cert data.
When the quit channel is closed this function unregisters the lease and closes the client.
Deprecated: It's best to use StartTLSRegistration because this function cannot re-register the lease in the unlikely event that the server somehow forgets the lease exists when it tries to renew, because it does not have access to the registration request. In that case this function gets stuck trying to renew forever. The server can't do it because it has forgetten in this scenario.
type Hostname ¶
type Hostname struct { Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` // contains filtered or unexported fields }
func (*Hostname) Descriptor
deprecated
func (*Hostname) GetHostname ¶
func (*Hostname) ProtoMessage ¶
func (*Hostname) ProtoMessage()
func (*Hostname) ProtoReflect ¶
func (x *Hostname) ProtoReflect() protoreflect.Message
type Lease ¶
type Lease struct { Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` Address string `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Timeout *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` // If generate_certificate was set in the request, this is the signed x509 // certificate to use for your server. It will be renewed with the lease. Certificate []byte `protobuf:"bytes,4,opt,name=Certificate,proto3" json:"Certificate,omitempty"` // contains filtered or unexported fields }
func MustStartRegistration ¶
func MustStartRegistration(request *RegisterRequest, quit <-chan struct{}) *Lease
The same as StartRegistration but call log.Fatal on error
func MustStartTLSRegistration ¶
func MustStartTLSRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, *tls.Config)
The same as StartTLSRegistration but call log.Fatal on error
func StartRegistration ¶
func StartRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, error)
Make a connection to the portal RPC service and send the registration request. Also starts a goroutine to renew (and potentially re-register) the lease until the quit channel is closed.
Returns the initial lease or an error if the registration didn't work.
func StartTLSRegistration ¶
func StartTLSRegistration(request *RegisterRequest, quit <-chan struct{}) (*Lease, *tls.Config, error)
Make a connection to the portal RPC service and send the registration request. Additionally, generate a new TLS certificate and request portal to sign it with portal's private Certificate Authority (the key is never sent over the network).
Starts a goroutine to renew the both the lease and the TLS certificate (and re-register the lease if necessary) until the quit channel is closed.
Returns the initial lease, and a tls.Config which automatically renews the certificate seemlessly. Or return an error if the registration didn't work.
func (*Lease) Descriptor
deprecated
func (*Lease) GetAddress ¶ added in v0.6.0
func (*Lease) GetCertificate ¶
func (*Lease) GetPattern ¶
func (*Lease) GetTimeout ¶
func (x *Lease) GetTimeout() *timestamppb.Timestamp
func (*Lease) ProtoMessage ¶
func (*Lease) ProtoMessage()
func (*Lease) ProtoReflect ¶
func (x *Lease) ProtoReflect() protoreflect.Message
type PortalClient ¶
type PortalClient interface { Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*Lease, error) Renew(ctx context.Context, in *Lease, opts ...grpc.CallOption) (*Lease, error) Unregister(ctx context.Context, in *Lease, opts ...grpc.CallOption) (*Lease, error) // Returns the address that will be used to connect to your server if // registered. It is necessary to register the correct hostname in the TLS // certificate signed by portal. MyHostname(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Hostname, error) }
PortalClient is the client API for Portal service.
For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
func NewPortalClient ¶
func NewPortalClient(cc grpc.ClientConnInterface) PortalClient
type PortalServer ¶
type PortalServer interface { Register(context.Context, *RegisterRequest) (*Lease, error) Renew(context.Context, *Lease) (*Lease, error) Unregister(context.Context, *Lease) (*Lease, error) // Returns the address that will be used to connect to your server if // registered. It is necessary to register the correct hostname in the TLS // certificate signed by portal. MyHostname(context.Context, *emptypb.Empty) (*Hostname, error) // contains filtered or unexported methods }
PortalServer is the server API for Portal service. All implementations must embed UnimplementedPortalServer for forward compatibility
type RegisterRequest ¶
type RegisterRequest struct { // For HTTP: A url pattern that works with http.DefaultServMux. Ex: /images/ // For TCP: ":tcp:port" for the port number portal should listen on. Only tcp // is accepted for now. // // HTTP patterns optionally accept a hostname (URL) constraint prefix. Or if // portal is configured to use the default hostname for no hostname patterns, // you can use * for the hostname to always match all URLs. For example: // // ask.systems/images/ // */favicon.ico Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` // TODO: maybe support multiple patterns for the same IP/port // Set for third party web interfaces (or TCP proxy backends) that can't // use an random lease port. // Must be outside the range of portal's automatic ports. FixedPort uint32 `protobuf:"varint,2,opt,name=fixed_port,json=fixedPort,proto3" json:"fixed_port,omitempty"` // Optional: If set, forward the requests for pattern to this IP/hostname. // If unset, forward requests to the IP that sent the RegisterRequest. // // It is easiest to just run assimilate on the machine you want the forwarding // rule for, but if you have a cpanel-only host or otherwise don't have access // to run arbitrary code then you can use this setting to run assimilate on // another machine. // // If you use this you need to be mindful of TLS and the network you're // sending the data over. Ideally you should set up a self-signed certificate // on the other machine and portal will detect TLS support. Otherwise make // sure you only use this with a trusted local network. Hostname string `protobuf:"bytes,6,opt,name=hostname,proto3" json:"hostname,omitempty"` // If true, remove the pattern in the URL of HTTP requests we forward to the // backend to hide that it is behind a reverse proxy. // // For example: If the pattern is /foo/ and the request is for /foo/index.html // - If true the request to the backend is /index.html // - If false the request to the backend is /foo/index.html // // Ignored for TCP proxies. StripPattern bool `protobuf:"varint,3,opt,name=strip_pattern,json=stripPattern,proto3" json:"strip_pattern,omitempty"` // If true, do not redirect HTTP requests to HTTPS. This means the data will // be sent in plain-text readable to anyone if the client requests it. Some // legacy systems require plain HTTP requests. Leave this off by default for // security, that way responses will only be readable by the client. // // Ignored for TCP proxies. AllowHttp bool `protobuf:"varint,5,opt,name=allow_http,json=allowHttp,proto3" json:"allow_http,omitempty"` // If set, the server will sign the certificate request with portal's // certificate as the root and accept connections to the signed cert. This way // network traffic behind the reverse proxy can be encrypted. CertificateRequest []byte `protobuf:"bytes,4,opt,name=certificate_request,json=certificateRequest,proto3" json:"certificate_request,omitempty"` // contains filtered or unexported fields }
func (*RegisterRequest) Descriptor
deprecated
func (*RegisterRequest) Descriptor() ([]byte, []int)
Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead.
func (*RegisterRequest) GetAllowHttp ¶ added in v0.5.1
func (x *RegisterRequest) GetAllowHttp() bool
func (*RegisterRequest) GetCertificateRequest ¶
func (x *RegisterRequest) GetCertificateRequest() []byte
func (*RegisterRequest) GetFixedPort ¶
func (x *RegisterRequest) GetFixedPort() uint32
func (*RegisterRequest) GetHostname ¶ added in v0.6.0
func (x *RegisterRequest) GetHostname() string
func (*RegisterRequest) GetPattern ¶
func (x *RegisterRequest) GetPattern() string
func (*RegisterRequest) GetStripPattern ¶
func (x *RegisterRequest) GetStripPattern() bool
func (*RegisterRequest) ProtoMessage ¶
func (*RegisterRequest) ProtoMessage()
func (*RegisterRequest) ProtoReflect ¶
func (x *RegisterRequest) ProtoReflect() protoreflect.Message
func (*RegisterRequest) Reset ¶
func (x *RegisterRequest) Reset()
func (*RegisterRequest) String ¶
func (x *RegisterRequest) String() string
type UnimplementedPortalServer ¶
type UnimplementedPortalServer struct { }
UnimplementedPortalServer must be embedded to have forward compatible implementations.
func (UnimplementedPortalServer) MyHostname ¶
func (UnimplementedPortalServer) Register ¶
func (UnimplementedPortalServer) Register(context.Context, *RegisterRequest) (*Lease, error)
func (UnimplementedPortalServer) Unregister ¶
type UnsafePortalServer ¶
type UnsafePortalServer interface {
// contains filtered or unexported methods
}
UnsafePortalServer may be embedded to opt out of forward compatibility for this service. Use of this interface is not recommended, as added methods to PortalServer will result in compilation errors.