vici

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2023 License: MIT Imports: 12 Imported by: 14

Documentation

Overview

Package vici implements a strongSwan vici protocol client. The Go package is documented here. For a complete overview and specification of the vici protocol visit:

https://www.strongswan.org/apidoc/md_src_libcharon_plugins_vici_README.html

The vici package has two important types that the API is built around. The first is the Session type, which provides communication to the charon daemon over the VICI protocol. This type provides methods for invoking commands on the daemon, and subscribing to server-issued events. Second is the Message type, which provides an abstraction of the data format transmitted over VICI. The Message type is essentially a map, where values can be either a string, list of strings ([]string), or a section/sub-message (*Message). The contents of a Message are most conveniently managed by creating a struct analogous to the message you need to send or receive in a command, and using the MarshalMessage and UnmarshalMessage functions with that struct. There are however, Get, Set, and Unset methods on the Message type for simpler messages.

In order to use this package, it is important to have a basic understanding of the VICI protocol as desribed in the link above. The 'Client-initiated Commands' section describes all of the available commands you can invoke using a Session. They are invoked by name, exactly as written in that document (excluding the parenthesis). For example, if you wanted to call the 'reload-settings' command, you would pass the string "reload-settings" as the first argument to Session.CommandRequest. Each command has its IN and OUT message parameters defined, i.e.:

{
    <message IN parameters>
} => {
    <message OUT parameters>
}

The Message that defines the IN parameters is passed as the second argument to Session.CommandRequest. If no IN parameters are required, you can safely pass nil instead. The Message returned by Session.CommandRequest will contain fields defined by the OUT parameters. Again, the Message type supports marshalling with the MarshalMessage and UnmarshalMessage functions. See the MarshalMessage function documentation for details.

Some commands will stream events while the command is active, such as the "list-sas" command. For such commands, the VICI protocol defines the event type that is streamed for the command. In the case of the "list-sas" command, the server will stream "list-sa" events. To invoke this type of command, use the Session.StreamedCommandRequest method, which accepts the event name as an argument in addition to the command name and IN message. For example, calling Session.StreamedCommandRequest("list-sas", "list-sa", msg) will return a list of messages including all "list-sa" events and the final response message.

In addition to Client-initiated commands, the VICI protocol has a server-issued events feature. An event is just a named message generated by the server, which is sent to any clients that have subscribed to that event type. The 'Server-issued events' section of the VICI protocol documentation lists all of the events that can be generated by the server, along with associated message parameters. The Session.Subscribe and Session.Unsubscribe methods are used to control which events the client will receive. For example, a client can subscribe to the 'ike-updown' and 'child-updown' events by calling Session.Subscribe("ike-updown", "child-updown"). To receive events, register a channel with Session.NotifyEvents.

For information on the semantics of VICI message parameters and how they control the strongSwan configuration, see the swanctl.conf documentation:

https://wiki.strongswan.org/projects/strongswan/wiki/Swanctlconf

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func UnmarshalMessage

func UnmarshalMessage(m *Message, v any) error

UnmarshalMessage unmarshals m to a map or struct (or struct pointer). When unmarshaling to a struct, only exported fields with a vici struct tag explicitly set are unmarshaled. Struct fields can be unmarshaled inline by providing the opt "inline" to the vici struct tag.

An error is returned if the underlying value of v cannot be unmarshaled into, or an unsupported type is encountered.

Example
type child struct {
	LocalTrafficSelectors []string `vici:"local_ts"`
	UpdownScript          string   `vici:"updown"`
	ESPProposals          []string `vici:"esp_proposals"`
}

m := NewMessage()

if err := m.Set("local_ts", []string{"10.1.0.0/16"}); err != nil {
	fmt.Println(err)
	return
}
if err := m.Set("esp_proposals", []string{"aes128gcm128-x25519"}); err != nil {
	fmt.Println(err)
	return
}
if err := m.Set("updown", "/usr/local/libexec/ipsec/_updown iptables"); err != nil {
	fmt.Println(err)
	return
}

var c child

if err := UnmarshalMessage(m, &c); err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%+v\n", c)
Output:

{LocalTrafficSelectors:[10.1.0.0/16] UpdownScript:/usr/local/libexec/ipsec/_updown iptables ESPProposals:[aes128gcm128-x25519]}

Types

type Event added in v0.4.0

type Event struct {
	// Name is the event type name as specified by the
	// charon server, such as "ike-updown" or "log".
	Name string

	// Message is the Message associated with this event.
	Message *Message

	// Timestamp holds the timestamp of when the client
	// received the event.
	Timestamp time.Time
}

Event represents an event received by a Session sent from the charon daemon. It contains an associated Message and corresponds to one of the event types registered with Session.Listen.

type Message

type Message struct {
	// contains filtered or unexported fields
}

Message represents a vici message as described in the vici README:

https://www.strongswan.org/apidoc/md_src_libcharon_plugins_vici_README.html

A message supports encoding key-value pairs, lists, and sub-sections (or sub-messages). Within a Message, each value, list, and sub-section is keyed by a string.

The value in a key-value pair is represented by a string, lists are represented by a string slice, and sub-sections are represented by *Message. When constructing a Message, other types may be used for convenience, and may have rules on how they are converted to an appropriate internal message element type. See Message.Set and MarshalMessage for details.

func MarshalMessage

func MarshalMessage(v any) (*Message, error)

MarshalMessage returns a Message encoded from v. The type of v must be either a map, struct, or struct pointer.

If v is a map, the map's key type must be a string, and the type of the corresponding map element must be supported by Message.Set or MarshalMessage itself.

If v is a struct or points to one, fields are only marshaled if they are exported and explicitly have a vici struct tag set. In these cases, the struct tag defines the key used for that field in the Message, and the field type must be supported by Message.Set or MarshalMessage itself. Embedded structs may be used as fields, either by explicitly giving them a field name with the vici struct tag, or by marking them as inline. Inlined structs are defined by using the opt "inline" on the vici tag, for example: `vici:",inline"`.

Any field that would be encoded as an empty message element is always omitted during marshaling. However, "empty message element" is not necessarily analogous to a type's zero value in Go. Because all supported Go types are marshaled to either string, []string, or *Message, a field is considered an empty message element if it would be encoded as either an empty string, zero-length []string or nil. On the other hand, an integer's zero value is 0, but this is marshaled to the string "0", and will not be omitted from marshaling. Likewise, a bool's zero value is false, which is marshaled to the string "no".

Example
type child struct {
	LocalTrafficSelectors []string `vici:"local_ts"`
	UpdownScript          string   `vici:"updown"`
	ESPProposals          []string `vici:"esp_proposals"`
}

type conn struct {
	LocalAddrs   []string         `vici:"local_addrs"`
	Local        map[string]any   `vici:"local"`
	Remote       map[string]any   `vici:"remote"`
	Children     map[string]child `vici:"children"`
	IKEVersion   uint             `vici:"version"`
	IKEProposals []string         `vici:"proposals"`
}

// Create a Message that represents the 'rw' connection from this swanctl.conf:
// https://www.strongswan.org/testing/testresults/swanctl/rw-cert/moon.swanctl.conf
rw := &conn{
	LocalAddrs: []string{"192.168.0.1"},
	Local: map[string]any{
		"auth":  "pubkey",
		"certs": []string{"moonCert.pem"},
		"id":    "moon.strongswan.org",
	},
	Remote: map[string]any{
		"auth": "pubkey",
	},
	Children: map[string]child{
		"net": {
			LocalTrafficSelectors: []string{"10.1.0.0/16"},
			UpdownScript:          "/usr/local/libexec/ipsec/_updown iptables",
			ESPProposals:          []string{"aes128gcm128-x25519"},
		},
	},
	IKEVersion:   2,
	IKEProposals: []string{"aes128-sha256-x25519"},
}

m, err := MarshalMessage(rw)
if err != nil {
	fmt.Println(err)
	return
}

fmt.Println(m.Get("proposals"))

net := m.Get("children").(*Message).Get("net").(*Message)
for _, k := range net.Keys() {
	fmt.Printf("%s: %s\n", k, net.Get(k))
}
Output:

[aes128-sha256-x25519]
local_ts: [10.1.0.0/16]
updown: /usr/local/libexec/ipsec/_updown iptables
esp_proposals: [aes128gcm128-x25519]

func NewMessage

func NewMessage() *Message

NewMessage returns an empty Message.

func (*Message) Err

func (m *Message) Err() error

Err examines a command response Message, and determines if it was successful. If it was, or if the message does not contain a 'success' field, nil is returned. Otherwise, an error is returned using the 'errmsg' field.

func (*Message) Get

func (m *Message) Get(key string) any

Get returns the value of the field identified by key, if it exists. If the field does not exist, nil is returned.

The value returned by Get is the internal message representation of that field, which means the type is either string, []string, or *Message.

func (*Message) Keys

func (m *Message) Keys() []string

Keys returns the list of valid message keys.

func (*Message) Set

func (m *Message) Set(key string, value any) error

Set sets key to value. An error is returned if the underlying type of v is not supported.

If the type of v is supported, it is represented in the message as either a string, []string, or *Message. The currently supported types are:

  • string
  • integer types (converted to string)
  • bool (where true and false are converted to the strings "yes" and "no", respectively)
  • []string
  • *Message
  • map (the map must be valid as per MarshalMessage)
  • struct (the struct must be valid as per MarshalMessage)

Pointer types of the above are allowed and can be used to differentiate between an unset value and a zero value. If a pointer is nil, it is not added to the message.

If the key already exists the value is overwritten, but the ordering of the message is not changed.

Example
m := NewMessage()

if err := m.Set("version", 2); err != nil {
	fmt.Println(err)
	return
}

if err := m.Set("mobike", false); err != nil {
	fmt.Println(err)
	return
}

if err := m.Set("local_addrs", []string{"192.168.0.1/24"}); err != nil {
	fmt.Println(err)
	return
}

fmt.Printf("%v, %v, %v\n", m.Get("version"), m.Get("mobike"), m.Get("local_addrs"))
Output:

2, no, [192.168.0.1/24]

func (*Message) Unset added in v0.3.0

func (m *Message) Unset(key string)

Unset unsets the message field identified by key. There is no effect if the key does not exist.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session is a vici client session.

func NewSession

func NewSession(opts ...SessionOption) (*Session, error)

NewSession returns a new vici session.

func (*Session) Close

func (s *Session) Close() error

Close closes the vici session.

func (*Session) CommandRequest

func (s *Session) CommandRequest(cmd string, msg *Message) (*Message, error)

CommandRequest sends a command request to the server, and returns the server's response. The command is specified by cmd, and its arguments are provided by msg. If there is an error communicating with the daemon, a nil Message and non-nil error are returned. If the command fails, the response Message is returned along with the error returned by Message.Err.

func (*Session) NotifyEvents added in v0.6.0

func (s *Session) NotifyEvents(c chan<- Event)

NotifyEvents registers c for writing received events. The Session must first subscribe to events using the Subscribe method.

Writes to c will not block: the caller must ensure that c has sufficient buffer space to keep up with the expected event rate. If the write to c would block, the event is discarded.

NotifyEvents may be called multiple times with different channels: each channel will indepedently receive a copy of each event received by the Session.

When the Session is Close()'d, or the event listener otherwise exits, e.g. due to the daemon stopping or restarting, c will be closed to indicate that no more events will be passed to it.

func (*Session) StopEvents added in v0.6.0

func (s *Session) StopEvents(c chan<- Event)

StopEvents stops writing received events to c.

func (*Session) StreamedCommandRequest

func (s *Session) StreamedCommandRequest(cmd string, event string, msg *Message) ([]*Message, error)

StreamedCommandRequest sends a streamed command request to the server. StreamedCommandRequest behaves like CommandRequest, but accepts an event argument, which specifies the event type to stream while the command request is active. The complete stream of messages received from the server is returned once the request is complete.

func (*Session) Subscribe added in v0.4.0

func (s *Session) Subscribe(events ...string) error

Subscribe registers the session to listen for all events given. To receive events that are registered here, use NotifyEvents. An error is returned if Subscribe is not able to register the given events with the charon daemon.

func (*Session) Unsubscribe added in v0.4.0

func (s *Session) Unsubscribe(events ...string) error

Unsubscribe unregisters the given events, so the session will no longer receive events of the given type. If a given event is not valid, an error is retured.

func (*Session) UnsubscribeAll added in v0.4.0

func (s *Session) UnsubscribeAll() error

UnsubscribeAll unregisters all events that the session is currently subscribed to.

type SessionOption added in v0.2.0

type SessionOption interface {
	// contains filtered or unexported methods
}

SessionOption is used to specify additional options to a Session.

func WithAddr added in v0.4.0

func WithAddr(network, addr string) SessionOption

WithAddr specifies the network and address of the socket that charon is listening on. If this option is not specified, the default path, /var/run/charon.vici, is used.

As the protocol itself currently does not provide any security or authentication properties, it is recommended to run it over a UNIX socket with appropriate permissions.

func WithDialContext added in v0.4.0

func WithDialContext(dialer func(ctx context.Context, network, addr string) (net.Conn, error)) SessionOption

WithDialContext specifies the dial func to use when dialing the charon socket.

func WithSocketPath added in v0.2.0

func WithSocketPath(path string) SessionOption

WithSocketPath specifies the path of the socket that charon is listening on. If this option is not specified, the default path, /var/run/charon.vici, is used.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL