Documentation
¶
Overview ¶
Package config loads locksql profiles from the project and user config files, applies defaults, refuses anything that looks like a secret, and computes the security-relevant policy of a profile.
Index ¶
- Constants
- Variables
- func ApprovedKey(root, profile string) string
- func DefaultDetectors() []string
- func DefaultPort(engine string) int
- func EncodeShare(w io.Writer, s Share) error
- func FindProjectRoot(cwd string) (string, bool)
- func Fingerprint(p Policy) string
- func IsLocalKey(key string) bool
- func IsLoopback(host string) bool
- func ParseProfiles(data []byte, name, baseDir string) (map[string]Profile, error)
- func ProjectHash(root string) string
- func ProjectKey(cwd string) string
- func ProxyString(argv []string) string
- func RuleSetOf(p Profile) string
- func SSHString(s *SSHProfile) string
- func SaveApproved(stateDir, key string, p Policy) error
- func StateDir() (string, error)
- func TLSRank(mode string) int
- func TLSVerifiesChain(mode, ca string) bool
- func UserConfigPath() (string, error)
- func ValidRuleSet(s string) bool
- type Change
- type Config
- type ConfigFile
- type ImportOptions
- type Limits
- type Policy
- type Profile
- type SSHProfile
- type Share
- type SharedLimits
- type SharedProfile
- type SharedRule
- type SharedRules
- type SharedSSH
- type Skipped
- type Tier
Constants ¶
const ( WriteFunctionsDenylist = "denylist" WriteFunctionsAllowlist = "allowlist" )
Values of write_functions. The default, denylist, is stored as "" so that policies approved before the setting existed keep their fingerprint.
const ( EngineMariaDB = "mariadb" EngineMySQL = "mysql" EnginePostgres = "postgres" EngineSQLite = "sqlite" )
Supported engines.
const ( MinIdleTimeout = time.Minute MaxMaxSession = 24 * time.Hour )
Bounds of the session limits: MinIdleTimeout <= idle_timeout <= max_session <= MaxMaxSession.
const ( TLSDisable = kconfig.TLSDisable TLSPrefer = kconfig.TLSPrefer TLSRequire = kconfig.TLSRequire TLSVerifyCA = kconfig.TLSVerifyCA TLSVerifyFull = kconfig.TLSVerifyFull )
TLS modes of a network profile, as libpq's sslmode, in increasing strength.
const ( CredentialsAsk = kconfig.CredentialsAsk CredentialsKeychain = kconfig.CredentialsKeychain CredentialsVault = kconfig.CredentialsVault )
Credentials modes. CredentialsVault reads the user and password from HashiCorp Vault with the vault CLI at every connection (Profile.VaultPath).
const ( SSHAuthKey = kconfig.SSHAuthKey SSHAuthAgent = kconfig.SSHAuthAgent SSHAuthPassword = kconfig.SSHAuthPassword )
SSH authentication methods.
const DefaultMaskMode = "redact"
DefaultMaskMode is the mode of a mask rule that sets none.
const DefaultSSHPort = kconfig.DefaultSSHPort
DefaultSSHPort is the port of an ssh table that leaves port unset.
ShareFormat is the version of the shared configuration file this release reads and writes. A newer file is refused; an older one is migrated in DecodeShare.
Variables ¶
ErrNotShareable marks a profile that cannot be shared: a SQLite file or a Unix socket is a path of one machine.
var HandLocalKeys = []string{"proxy_command"}
HandLocalKeys are local keys that only the importer's own config file may set: proxy_command is a command line, which neither a shared file nor --local may bring. An import keeps the local value, or skips the profile.
var LocalKeys = []string{"tls_ca", "tls_cert", "tls_key", "ssh.key"}
LocalKeys are the keys that name files of one machine: never exported, listed in needs_local, given on import with --local NAME.KEY=VALUE.
Functions ¶
func ApprovedKey ¶
ApprovedKey names the approved-policy file of a profile: <ProjectHash(root)>-<profile>, or user-<profile> for a profile that comes from the user config only (root == "").
func DefaultDetectors ¶
func DefaultDetectors() []string
DefaultDetectors is the detector set used when a profile does not set detectors. National id detectors are opt-in.
func DefaultPort ¶ added in v0.5.0
DefaultPort is the port of engine when a profile leaves port unset, 0 for an engine without one (sqlite).
func EncodeShare ¶ added in v0.9.0
EncodeShare writes s as indented JSON, keys sorted.
func FindProjectRoot ¶
FindProjectRoot walks up from cwd to the first directory holding .locksql/config.toml.
func Fingerprint ¶
Fingerprint returns the hex sha256 of the policy's canonical JSON. List order does not change the fingerprint. Only an out-of-range Tier can fail to encode; lockkit then fingerprints its raw form so that it still differs from every valid policy.
func IsLocalKey ¶ added in v0.9.0
IsLocalKey reports whether key is one of LocalKeys.
func IsLoopback ¶ added in v0.4.0
IsLoopback reports "localhost" or a loopback IP literal.
func ParseProfiles ¶ added in v0.1.2
ParseProfiles decodes and validates config data. name labels errors. Relative sqlite paths are resolved against baseDir. Errors never quote config values that could be secrets.
func ProjectHash ¶
ProjectHash returns the first 8 hex digits of sha256 of the absolute, cleaned project root.
func ProjectKey ¶
ProjectKey names the project of cwd in socket paths: ProjectHash of the project root, or "user" when cwd is outside any project (profiles from the user config only). The console and its clients must agree on it.
func ProxyString ¶ added in v0.9.0
ProxyString renders a proxy_command argv for the approval screens, each argument quoted so that one holding a space cannot read as two.
func RuleSetOf ¶ added in v0.9.0
RuleSetOf is the PII rule set of p from its config: pii_rules, or the profile name in lower case. Sharing a set is opt-in.
func SSHString ¶ added in v0.4.0
func SSHString(s *SSHProfile) string
SSHString shows a bastion in a policy change.
func SaveApproved ¶
SaveApproved atomically writes the policy to <stateDir>/locksql/approved/<key>.json with mode 0600.
func StateDir ¶
StateDir returns the user state directory (without the locksql suffix): $XDG_STATE_HOME or ~/.local/state on Linux and other Unix systems, and os.UserConfigDir() on macOS.
func TLSVerifiesChain ¶ added in v0.4.0
TLSVerifiesChain reports a tls mode that verifies the server's certificate chain: verify-ca and verify-full, and require with tls_ca.
func UserConfigPath ¶ added in v0.1.2
UserConfigPath returns <user config dir>/locksql/config.toml.
func ValidRuleSet ¶ added in v0.9.0
ValidRuleSet reports whether s may name a PII rule set.
Types ¶
type Change ¶
Change is one difference between two policies.
func Diff ¶
Diff lists the changes from approved to current and marks each loosening (spec section 4): a higher tier, production true→false, a larger limit (0 means unlimited), a longer idle_timeout or max_session, a removed PII rule or detector, an added allow rule, any change of engine, host, port, path, database or user, and a credentials mode other than ask, a weaker tls mode, any change of tls_ca, tls_cert, tls_key or tls_server_name, any change of the ssh table or of proxy_command, any change of the PII rule set, and write_functions from allowlist to denylist.
type Config ¶
type Config struct {
// ProjectRoot is the directory holding .locksql/config.toml, or "" when
// no project config was found.
ProjectRoot string
Profiles map[string]Profile
}
Config is the merged set of profiles visible from a working directory.
type ConfigFile ¶ added in v0.9.0
type ConfigFile struct {
// contains filtered or unexported fields
}
ConfigFile is a config file as written, for editing: profiles the edit does not touch are written back with the keys they had.
func ParseConfigFile ¶ added in v0.9.0
func ParseConfigFile(data []byte, name, baseDir string) (*ConfigFile, error)
ParseConfigFile decodes config data for editing. It is checked like ParseProfiles; name labels errors and baseDir resolves sqlite paths.
func (*ConfigFile) Import ¶ added in v0.9.0
func (f *ConfigFile) Import(s *Share, o ImportOptions) ([]Skipped, error)
Import merges the profiles of s into f. An imported profile replaces the profile of the same name, but keeps its user (unless the share sets one) and its local keys (unless --local sets them). A profile whose needs_local keys are neither given nor kept is skipped. --local values for a key that is not local, or for a profile not imported, are refused.
func (*ConfigFile) Names ¶ added in v0.9.0
func (f *ConfigFile) Names() []string
Names lists the profiles of f, sorted.
func (*ConfigFile) TOML ¶ added in v0.9.0
func (f *ConfigFile) TOML(header string) []byte
TOML writes f as a config file: header, then one table per profile, sorted, with only the keys a profile sets. Every string is quoted and escaped; values were checked for control characters on the way in, and the result is parsed again by the caller before it is written.
type ImportOptions ¶ added in v0.9.0
type ImportOptions struct {
// Only, when not empty, limits the import to these profiles.
Only []string
// Replace drops the profiles of the file that the import does not
// hold; by default they are kept.
Replace bool
// Local are the --local values: profile -> key -> value.
Local map[string]map[string]string
}
ImportOptions steer ConfigFile.Import.
type Limits ¶
type Limits struct {
StatementTimeout time.Duration `toml:"statement_timeout" json:"statement_timeout"`
ExplainRowsWarn int64 `toml:"explain_rows_warn" json:"explain_rows_warn"`
ExplainRowsRefuse int64 `toml:"explain_rows_refuse" json:"explain_rows_refuse"`
MaxRows int `toml:"max_rows" json:"max_rows"`
MaxCellChars int `toml:"max_cell_chars" json:"max_cell_chars"`
MaxOutputBytes int `toml:"max_output_bytes" json:"max_output_bytes"`
// ExplainCostRefuse refuses a plan whose total cost, in the engine's
// own units, is above it; 0 disables the check. Engines without a cost
// (SQLite) are not checked.
ExplainCostRefuse float64 `toml:"explain_cost_refuse" json:"explain_cost_refuse,omitempty"`
// KAnonymity is the smallest number of rows a PII filter, a grouping
// on a PII column or an aggregate of one may cover without a warning
// on the approval screen.
KAnonymity int `toml:"k_anonymity" json:"k_anonymity,omitempty"`
// ReferenceProbe has no effect since cell references are not
// supported; it is still read, so that existing configs load, and
// raising it is still a loosening.
ReferenceProbe int `toml:"reference_probe" json:"reference_probe,omitempty"`
// IdleTimeout ends the console session after that long without
// activity, MaxSession after that long in all; raising either is a
// loosening. Bounds: 1m <= IdleTimeout <= MaxSession <= 24h.
IdleTimeout time.Duration `toml:"idle_timeout" json:"idle_timeout,omitempty"`
MaxSession time.Duration `toml:"max_session" json:"max_session,omitempty"`
}
Limits bound the cost and size of one statement.
func DefaultLimits ¶
DefaultLimits returns the limits applied to unset (zero) limit fields.
type Policy ¶
type Policy struct {
Profile Profile `json:"profile"`
PIIMask []string `json:"pii_mask"` // sorted column patterns
PIIAllow []string `json:"pii_allow"` // sorted column patterns
// PIIModes maps a mask pattern to its mode when it is not the default
// (redact): partial or email.
PIIModes map[string]string `json:"pii_modes,omitempty"`
}
Policy is the security-relevant view of one profile plus its PII rules. The console compares the current policy with the last approved one and asks the human before applying any loosening.
func Canonical ¶ added in v0.11.0
Canonical is p in its canonical form: sorted lists, defaults applied. Its JSON is what is saved as the approved policy and fingerprinted.
func LoadApproved ¶
LoadApproved reads <stateDir>/locksql/approved/<key>.json. When no policy was approved yet, the error wraps fs.ErrNotExist.
type Profile ¶
type Profile struct {
Name string `json:"name"`
Engine string `json:"engine"`
Host string `json:"host"`
Path string `json:"path"`
User string `json:"user"`
Database string `json:"database"`
Credentials string `json:"credentials"`
// TLS is the transport mode (TLSDisable ... TLSVerifyFull) and TLSCA an
// optional PEM bundle that replaces the system roots.
TLS string `json:"tls,omitempty"`
TLSCA string `json:"tls_ca,omitempty"`
// TLSCert and TLSKey are the PEM files of a client certificate and its
// private key, presented when the server asks for one. TLSServerName
// replaces host as the name verify-full checks in the certificate.
TLSCert string `json:"tls_cert,omitempty"`
TLSKey string `json:"tls_key,omitempty"`
TLSServerName string `json:"tls_server_name,omitempty"`
// SSH, when set, is the bastion the profile is reached through.
SSH *SSHProfile `json:"ssh,omitempty"`
// ProxyCommand, when set, is the argv of a command whose stdin and
// stdout carry each connection to the database, as OpenSSH's
// ProxyCommand: docker exec, kubectl exec, ssh to a socket and so on.
// It runs without a shell, as the console account.
ProxyCommand []string `json:"proxy_command,omitempty"`
Port int `json:"port"`
Tier Tier `json:"tier"`
Production bool `json:"production"`
Detectors []string `json:"detectors"`
Limits Limits `json:"limits"`
// CredentialsTTL makes the console ask for the secret again (and
// reconnect) once the connection is that old; 0 keeps it for the
// session. It suits short-lived secrets from a vault.
CredentialsTTL time.Duration `json:"credentials_ttl,omitempty"`
// VaultPath is the Vault path read for the login when Credentials is
// "vault", such as "database/creds/app-readonly".
VaultPath string `json:"vault_path,omitempty"`
// PIIRules names the PII rule set of the profile: the file
// pii/<set>.toml next to the config. The config holds the pii_rules
// key, "" when it is not set; the console stores the set it resolved
// (RuleSetOf, or the migrated default set) in the policy it approves.
PIIRules string `json:"pii_rules,omitempty"`
// WriteFunctions is "" (the denylist of every class) or
// WriteFunctionsAllowlist: writes may call allowlisted functions only.
WriteFunctions string `json:"write_functions,omitempty"`
}
Profile is one named database target with its policy. It never holds a secret.
type SSHProfile ¶ added in v0.4.0
type SSHProfile = kconfig.SSHProfile
SSHProfile is the bastion a network profile is reached through.
type Share ¶ added in v0.9.0
type Share struct {
}
Share is a shareable locksql configuration (locksql config export): the shared keys of some profiles and the PII rule sets they use, keyed by set name. It never holds a secret nor a machine-specific value.
func DecodeShare ¶ added in v0.9.0
DecodeShare reads a shared configuration strictly: an unknown field at any depth, a format newer than ShareFormat, a profile or set name that is not valid, a secret-like or unsafe value, a needs_local key that is not local, or trailing data are refused. Errors never quote a value.
type SharedLimits ¶ added in v0.9.0
type SharedLimits struct {
}
SharedLimits are the limits of a SharedProfile; durations as Go duration strings.
type SharedProfile ¶ added in v0.9.0
type SharedProfile struct {
}
SharedProfile holds the keys of a profile that a team shares. The keys of one machine (LocalKeys) are never in it: NeedsLocal names those the source profile had, which the importer supplies with --local. User is exported only on request: it is the employee's own login.
func ExportProfile ¶ added in v0.9.0
func ExportProfile(p Profile, set string, includeUser bool) (SharedProfile, error)
ExportProfile is the shared form of the resolved profile p, whose PII rule set is set. It fails with ErrNotShareable for a SQLite or Unix socket profile, and refuses a value that looks like a secret.
type SharedRule ¶ added in v0.9.0
type SharedRule struct {
}
SharedRule is one PII rule: a column pattern and, for a mask rule, its mode when it is not redact.
type SharedRules ¶ added in v0.9.0
type SharedRules struct {
}
SharedRules is one PII rule set in a Share.
type SharedSSH ¶ added in v0.9.0
type SharedSSH struct {
}
SharedSSH is the bastion of a SharedProfile, without its key file.
type Skipped ¶ added in v0.9.0
type Skipped struct{ Profile, Reason string }
Skipped is a profile of the share that was not imported, and why.
type Tier ¶
type Tier int
Tier is the highest statement class a profile may run. The order is TierRead < TierWrite < TierDDL < TierAdmin.
func (Tier) MarshalText ¶
MarshalText encodes the tier by name.
func (*Tier) UnmarshalText ¶
UnmarshalText decodes a tier name.