Certificates for other hosts: jarl cert get
jarld obtains and renews a certificate for another host; the host asks
for it with one command, waits, downloads it when it is newer than its
copy and runs a hook with the paths in JARL_CERT_* variables.
- Certificates have an owner user; another user can't take the name.
An existing one is obtained again only when a name is added or
removed. A new certificates grant (names=) says which names a user
may have; it is never part of "*". Users may own
certificates.max_per_user certificates (10) or their own cert_limit.
- POST /certificates/{name}/request; owners may list, show, export and
delete theirs; keys only over HTTPS, the socket or loopback; every
download is recorded and shown as "Pulled by".
- CLI: jarl cert get (saved settings, all saved when no name), cert
saved, cert forget, cert schedule (daily systemd timer, Linux, root).
- Migration 14. Tests: server, auth, and e2e against Pebble.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b4bb99b482
commit
9db76a113c
29 files changed
+1568
-40
No files matched your search
@@ -134,3 +134,23 @@ func TestRoleTemplatesValid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificateGrants(t *testing.T) {
|
||||
g, err := ParseGrant("certificates:write names=*.adbraun.lol")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !GrantAllows(g, Action{Resource: ResCerts, Write: true, Name: "mail.adbraun.lol"}) || GrantAllows(g, Action{Resource: ResCerts, Write: true, Name: "mail.example.org"}) {
|
||||
t.Error("names filter")
|
||||
}
|
||||
if _, err := ParseGrant("certificates:read types=TXT"); err == nil {
|
||||
t.Error("types on certificates accepted")
|
||||
}
|
||||
if _, err := ParseGrant("certificates:read zones=adbraun.lol"); err == nil {
|
||||
t.Error("zones on certificates accepted")
|
||||
}
|
||||
// "*" never covers certificates: they hand out private keys.
|
||||
if GrantAllows(model.Grant{Resource: ResAll, Access: Write}, Action{Resource: ResCerts, Name: "mail.adbraun.lol"}) {
|
||||
t.Error("* covers certificates")
|
||||
}
|
||||
}
|
||||
+13
-4
@@ -21,11 +21,14 @@ const (
|
||||
ResAudit = "audit" // audit log and everyone's changesets
|
||||
ResStats = "stats" // query statistics
|
||||
ResBlocking = "blocking" // blocklists and block/allow rules
|
||||
ResAll = "*"
|
||||
// ResCerts is certificates for other hosts (jarl cert get), by the
|
||||
// names in them. It is never part of "*": it hands out private keys.
|
||||
ResCerts = "certificates"
|
||||
ResAll = "*"
|
||||
)
|
||||
|
||||
// Resources lists the resources in display order.
|
||||
var Resources = []string{ResZones, ResRecords, ResViews, ResACLs, ResTSIG, ResOptions, ResAudit, ResStats, ResBlocking}
|
||||
var Resources = []string{ResZones, ResRecords, ResViews, ResACLs, ResTSIG, ResOptions, ResAudit, ResStats, ResBlocking, ResCerts}
|
||||
|
||||
// Access levels.
|
||||
const (
|
||||
@@ -74,8 +77,11 @@ func ValidateGrant(g *model.Grant) error {
|
||||
for i, t := range g.Types {
|
||||
g.Types[i] = strings.ToUpper(t)
|
||||
}
|
||||
if (len(g.Names) > 0 || len(g.Types) > 0) && g.Resource != ResRecords {
|
||||
return fmt.Errorf("names and types filters only apply to the records resource")
|
||||
if len(g.Types) > 0 && g.Resource != ResRecords {
|
||||
return fmt.Errorf("the types filter only applies to the records resource")
|
||||
}
|
||||
if len(g.Names) > 0 && g.Resource != ResRecords && g.Resource != ResCerts {
|
||||
return fmt.Errorf("the names filter only applies to the records and certificates resources")
|
||||
}
|
||||
if (len(g.Zones) > 0 || len(g.Views) > 0) && g.Resource != ResRecords && g.Resource != ResZones && g.Resource != ResViews {
|
||||
return fmt.Errorf("zone and view filters only apply to the zones, records and views resources")
|
||||
@@ -228,6 +234,9 @@ func GrantAllows(g model.Grant, a Action) bool {
|
||||
if g.Resource != ResAll && g.Resource != a.Resource {
|
||||
return false
|
||||
}
|
||||
if g.Resource == ResAll && a.Resource == ResCerts {
|
||||
return false // only an explicit certificates grant
|
||||
}
|
||||
if a.Write && g.Access != Write {
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user