initial commit: certmanager
migrate from python to golang
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// AuthMethod selects how certmanager authenticates to Vault.
|
||||
type AuthMethod string
|
||||
|
||||
const (
|
||||
AuthMethodAppRole AuthMethod = "approle"
|
||||
AuthMethodLDAP AuthMethod = "ldap"
|
||||
AuthMethodKubernetes AuthMethod = "kubernetes"
|
||||
AuthMethodToken AuthMethod = "token"
|
||||
)
|
||||
|
||||
// Config is the top-level configuration structure.
|
||||
type Config struct {
|
||||
Vault VaultConfig `yaml:"vault"`
|
||||
}
|
||||
|
||||
// VaultConfig holds Vault connection and auth parameters.
|
||||
// Only the fields relevant to the chosen AuthMethod need to be populated.
|
||||
type VaultConfig struct {
|
||||
Addr string `yaml:"addr"`
|
||||
AuthMethod AuthMethod `yaml:"auth_method"` // approle | ldap | kubernetes | token
|
||||
MountPoint string `yaml:"mount_point"`
|
||||
RoleName string `yaml:"role_name"`
|
||||
OutputPath string `yaml:"output_path"`
|
||||
|
||||
// approle
|
||||
ApprolePath string `yaml:"approle_path"`
|
||||
RoleID string `yaml:"role_id"`
|
||||
SecretID string `yaml:"secret_id"`
|
||||
|
||||
// ldap
|
||||
LDAPPath string `yaml:"ldap_path"`
|
||||
LDAPUsername string `yaml:"ldap_username"`
|
||||
LDAPPassword string `yaml:"ldap_password"`
|
||||
|
||||
// kubernetes
|
||||
KubernetesPath string `yaml:"kubernetes_path"`
|
||||
KubernetesRole string `yaml:"kubernetes_role"`
|
||||
KubernetesTokenFile string `yaml:"kubernetes_token_file"`
|
||||
|
||||
// token (static; useful for testing or bootstrap)
|
||||
Token string `yaml:"token"`
|
||||
}
|
||||
|
||||
// DefaultPath returns the XDG-compliant default config file path:
|
||||
// $XDG_CONFIG_HOME/certmanager/config.yaml, falling back to
|
||||
// $HOME/.config/certmanager/config.yaml.
|
||||
func DefaultPath() string {
|
||||
base := os.Getenv("XDG_CONFIG_HOME")
|
||||
if base == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "config.yaml"
|
||||
}
|
||||
base = filepath.Join(home, ".config")
|
||||
}
|
||||
return filepath.Join(base, "certmanager", "config.yaml")
|
||||
}
|
||||
|
||||
// Load reads and parses the config file at the given path.
|
||||
func Load(path string) (*Config, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open config %q: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var cfg Config
|
||||
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("parse config %q: %w", path, err)
|
||||
}
|
||||
|
||||
// Default to approle for backwards-compatibility with the existing
|
||||
// Python certmanager/sshsignhost config format.
|
||||
if cfg.Vault.AuthMethod == "" {
|
||||
cfg.Vault.AuthMethod = AuthMethodAppRole
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultPath_XDGSet(t *testing.T) {
|
||||
t.Setenv("XDG_CONFIG_HOME", "/tmp/xdg")
|
||||
got := DefaultPath()
|
||||
want := "/tmp/xdg/certmanager/config.yaml"
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultPath_XDGUnset(t *testing.T) {
|
||||
t.Setenv("XDG_CONFIG_HOME", "")
|
||||
home, _ := os.UserHomeDir()
|
||||
got := DefaultPath()
|
||||
want := filepath.Join(home, ".config", "certmanager", "config.yaml")
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_AppRole(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
path := filepath.Join(tmp, "config.yaml")
|
||||
content := `
|
||||
vault:
|
||||
addr: https://vault.example.com:8200
|
||||
auth_method: approle
|
||||
approle_path: approle
|
||||
role_id: my-role-id
|
||||
mount_point: pki_int
|
||||
role_name: servers_default
|
||||
output_path: /tmp/certs
|
||||
`
|
||||
os.WriteFile(path, []byte(content), 0o644)
|
||||
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
if cfg.Vault.AuthMethod != AuthMethodAppRole {
|
||||
t.Errorf("auth_method = %q", cfg.Vault.AuthMethod)
|
||||
}
|
||||
if cfg.Vault.RoleID != "my-role-id" {
|
||||
t.Errorf("role_id = %q", cfg.Vault.RoleID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_DefaultAuthMethod(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
path := filepath.Join(tmp, "config.yaml")
|
||||
// Omit auth_method — should default to approle for backwards compat.
|
||||
content := `
|
||||
vault:
|
||||
addr: https://vault.example.com:8200
|
||||
role_id: my-role-id
|
||||
approle_path: approle
|
||||
mount_point: pki_int
|
||||
role_name: servers_default
|
||||
`
|
||||
os.WriteFile(path, []byte(content), 0o644)
|
||||
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
if cfg.Vault.AuthMethod != AuthMethodAppRole {
|
||||
t.Errorf("expected default approle, got %q", cfg.Vault.AuthMethod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_LDAP(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
path := filepath.Join(tmp, "config.yaml")
|
||||
content := `
|
||||
vault:
|
||||
addr: https://vault.example.com:8200
|
||||
auth_method: ldap
|
||||
ldap_path: ldap
|
||||
ldap_username: alice
|
||||
ldap_password: secret
|
||||
mount_point: pki_int
|
||||
role_name: servers_default
|
||||
`
|
||||
os.WriteFile(path, []byte(content), 0o644)
|
||||
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
if cfg.Vault.AuthMethod != AuthMethodLDAP {
|
||||
t.Errorf("auth_method = %q", cfg.Vault.AuthMethod)
|
||||
}
|
||||
if cfg.Vault.LDAPUsername != "alice" {
|
||||
t.Errorf("ldap_username = %q", cfg.Vault.LDAPUsername)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_Kubernetes(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
path := filepath.Join(tmp, "config.yaml")
|
||||
content := `
|
||||
vault:
|
||||
addr: https://vault.example.com:8200
|
||||
auth_method: kubernetes
|
||||
kubernetes_path: kubernetes
|
||||
kubernetes_role: puppet
|
||||
kubernetes_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
mount_point: pki_int
|
||||
role_name: servers_default
|
||||
`
|
||||
os.WriteFile(path, []byte(content), 0o644)
|
||||
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
if cfg.Vault.AuthMethod != AuthMethodKubernetes {
|
||||
t.Errorf("auth_method = %q", cfg.Vault.AuthMethod)
|
||||
}
|
||||
if cfg.Vault.KubernetesRole != "puppet" {
|
||||
t.Errorf("kubernetes_role = %q", cfg.Vault.KubernetesRole)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_Token(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
path := filepath.Join(tmp, "config.yaml")
|
||||
content := `
|
||||
vault:
|
||||
addr: https://vault.example.com:8200
|
||||
auth_method: token
|
||||
token: hvs.statictoken
|
||||
mount_point: pki_int
|
||||
role_name: servers_default
|
||||
`
|
||||
os.WriteFile(path, []byte(content), 0o644)
|
||||
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
if cfg.Vault.AuthMethod != AuthMethodToken {
|
||||
t.Errorf("auth_method = %q", cfg.Vault.AuthMethod)
|
||||
}
|
||||
if cfg.Vault.Token != "hvs.statictoken" {
|
||||
t.Errorf("token = %q", cfg.Vault.Token)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoad_MissingFile(t *testing.T) {
|
||||
_, err := Load("/nonexistent/config.yaml")
|
||||
if err == nil {
|
||||
t.Error("expected error for missing file, got nil")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user