Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 60012be942 |
12 changed files with 446 additions and 20 deletions
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"git.giftfish.de/ston1th/keyctl/pkg/api"
|
"git.giftfish.de/ston1th/keyctl/pkg/api"
|
||||||
"git.giftfish.de/ston1th/keyctl/pkg/cli"
|
"git.giftfish.de/ston1th/keyctl/pkg/cli"
|
||||||
"git.giftfish.de/ston1th/keyctl/pkg/db"
|
"git.giftfish.de/ston1th/keyctl/pkg/db"
|
||||||
|
"git.giftfish.de/ston1th/keyctl/pkg/ssh"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -41,6 +42,10 @@ func usage() {
|
||||||
keyctl approve - approve a key request (admin)
|
keyctl approve - approve a key request (admin)
|
||||||
keyctl reject - reject a key request (admin)
|
keyctl reject - reject a key request (admin)
|
||||||
keyctl del - delete a key (admin)
|
keyctl del - delete a key (admin)
|
||||||
|
keyctl ssh login - request ssh access
|
||||||
|
keyctl ssh req - list current ssh requests (admin)
|
||||||
|
keyctl ssh approve - approve ssh access (admin)
|
||||||
|
keyctl ssh reject - reject ssh access (admin)
|
||||||
keyctl help - show this usage
|
keyctl help - show this usage
|
||||||
`, version)
|
`, version)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
@ -56,6 +61,11 @@ func main() {
|
||||||
cli.Run(arg) // does not return
|
cli.Run(arg) // does not return
|
||||||
case "server":
|
case "server":
|
||||||
server() // does not return
|
server() // does not return
|
||||||
|
case "ssh":
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
usage() // does not return
|
||||||
|
}
|
||||||
|
ssh.Run(os.Args[2]) // does not return
|
||||||
default:
|
default:
|
||||||
usage() // does not return
|
usage() // does not return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,9 @@ func (c *Client) FollowRedirectLoop(r *http.Request, v any, delay time.Duration)
|
||||||
rdr, ok := err.(Redirect)
|
rdr, ok := err.(Redirect)
|
||||||
if err != nil && !ok {
|
if err != nil && !ok {
|
||||||
if errors.Is(err, types.ErrISE) ||
|
if errors.Is(err, types.ErrISE) ||
|
||||||
errors.Is(err, types.ErrKeyNotFound) {
|
errors.Is(err, types.ErrKeyNotFound) ||
|
||||||
|
errors.Is(err, types.ErrAccessReqNotFound) ||
|
||||||
|
errors.Is(err, types.ErrAccessRequestRejected) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errors.Is(err, types.ErrReqNotFound) ||
|
if errors.Is(err, types.ErrReqNotFound) ||
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ func NewServer(log logr.Logger, listen, socket string, db *db.DB) (*Server, erro
|
||||||
log: log,
|
log: log,
|
||||||
data: &types.ContextData{
|
data: &types.ContextData{
|
||||||
Approver: types.NewApprover(),
|
Approver: types.NewApprover(),
|
||||||
|
SSH: types.NewSSH(),
|
||||||
DB: db,
|
DB: db,
|
||||||
},
|
},
|
||||||
srv: &http.Server{
|
srv: &http.Server{
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
type ContextData struct {
|
type ContextData struct {
|
||||||
Approver *Approver
|
Approver *Approver
|
||||||
|
SSH *SSH
|
||||||
DB *db.DB
|
DB *db.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +98,10 @@ func (c *Context) Approver() *Approver {
|
||||||
return c.data.Approver
|
return c.data.Approver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) SSH() *SSH {
|
||||||
|
return c.data.SSH
|
||||||
|
}
|
||||||
|
|
||||||
// OK is a empty HTTP 200 JSON response
|
// OK is a empty HTTP 200 JSON response
|
||||||
func (c *Context) OK() {
|
func (c *Context) OK() {
|
||||||
c.log()
|
c.log()
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,10 @@ var (
|
||||||
ErrNotFound = newError("not found", http.StatusNotFound)
|
ErrNotFound = newError("not found", http.StatusNotFound)
|
||||||
ErrKeyNotFound = newError("key not found", http.StatusNotFound)
|
ErrKeyNotFound = newError("key not found", http.StatusNotFound)
|
||||||
ErrReqNotFound = newError("key request not found", http.StatusNotFound)
|
ErrReqNotFound = newError("key request not found", http.StatusNotFound)
|
||||||
|
ErrAccessReqNotFound = newError("ssh request not found", http.StatusNotFound)
|
||||||
ErrTooManyApprovals = newError("too many approvals pending", http.StatusConflict)
|
ErrTooManyApprovals = newError("too many approvals pending", http.StatusConflict)
|
||||||
ErrKeyRequestRejected = newError("key request rejected", http.StatusForbidden)
|
ErrKeyRequestRejected = newError("key request rejected", http.StatusForbidden)
|
||||||
|
ErrAccessRequestRejected = newError("ssh request rejected", http.StatusForbidden)
|
||||||
ErrMNA = newError("method not allowed", http.StatusMethodNotAllowed)
|
ErrMNA = newError("method not allowed", http.StatusMethodNotAllowed)
|
||||||
ErrISE = newError("internal server error", http.StatusInternalServerError)
|
ErrISE = newError("internal server error", http.StatusInternalServerError)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
99
pkg/api/types/ssh.go
Normal file
99
pkg/api/types/ssh.go
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright (C) 2023 Marius Schellenberger
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maps"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.giftfish.de/ston1th/keyctl/pkg/key"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SSHMap map[string]SSHAccess
|
||||||
|
|
||||||
|
type SSH struct {
|
||||||
|
// protects m
|
||||||
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
m SSHMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSSH() *SSH {
|
||||||
|
return &SSH{
|
||||||
|
m: make(SSHMap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SSH) New(host, user, ip string) (aid string, err error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
size := len(s.m)
|
||||||
|
if size > maxSize {
|
||||||
|
err = ErrTooManyApprovals
|
||||||
|
return
|
||||||
|
}
|
||||||
|
aid, err = key.GenerateID()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.m[aid] = SSHAccess{
|
||||||
|
Host: host,
|
||||||
|
User: user,
|
||||||
|
IP: ip,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SSH) List() (m SSHMap) {
|
||||||
|
s.mu.RLock()
|
||||||
|
m = maps.Clone(s.m)
|
||||||
|
s.mu.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *SSH) Status(aid, host, user, ip string) (status Status) {
|
||||||
|
s.mu.RLock()
|
||||||
|
if app, ok := s.m[aid]; ok {
|
||||||
|
if app.Host == host && app.IP == ip && app.User == user {
|
||||||
|
status = app.Status
|
||||||
|
} else {
|
||||||
|
status = Rejected
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = NotFound
|
||||||
|
}
|
||||||
|
s.mu.RUnlock()
|
||||||
|
if status == Approved || status == Rejected {
|
||||||
|
s.mu.Lock()
|
||||||
|
delete(s.m, aid)
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SSH) Update(aid string, status Status) (err error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
if app, ok := s.m[aid]; ok {
|
||||||
|
app.Status = status
|
||||||
|
s.m[aid] = app
|
||||||
|
go s.cleanup(aid)
|
||||||
|
} else {
|
||||||
|
err = ErrAccessReqNotFound
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SSH) cleanup(aid string) {
|
||||||
|
time.Sleep(time.Second * 10)
|
||||||
|
s.mu.Lock()
|
||||||
|
delete(s.m, aid)
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSHAccess struct {
|
||||||
|
Host string
|
||||||
|
User string
|
||||||
|
IP string
|
||||||
|
Status Status
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ var (
|
||||||
ErrClientIsNil = errors.New("client is nil")
|
ErrClientIsNil = errors.New("client is nil")
|
||||||
keyPath = schema.KeyPath
|
keyPath = schema.KeyPath
|
||||||
reqPath = schema.ReqPath
|
reqPath = schema.ReqPath
|
||||||
|
accessPath = schema.AccessPath
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
|
@ -121,3 +122,45 @@ func (c *Client) DeleteKey(ctx context.Context, id string) (err error) {
|
||||||
_, err = c.c.Do(req, nil)
|
_, err = c.c.Do(req, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) Access(ctx context.Context, host, user string) (ok bool, err error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/%s", accessPath, host, user)
|
||||||
|
req, err := c.c.NewRequest(ctx, "GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.c.FollowRedirectLoop(req, &ok, time.Second*2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetAccess(ctx context.Context) (accessList schema.AccessList, err error) {
|
||||||
|
path := accessPath
|
||||||
|
req, err := c.c.NewRequest(ctx, "GET", path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.c.Do(req, &accessList)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RejectAccess(ctx context.Context, id string) error {
|
||||||
|
return c.sendAccess(ctx, id, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) AcceptAccess(ctx context.Context, id string) error {
|
||||||
|
return c.sendAccess(ctx, id, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendAccess(ctx context.Context, id string, approve bool) (err error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", accessPath, id)
|
||||||
|
method := "DELETE"
|
||||||
|
if approve {
|
||||||
|
method = "PUT"
|
||||||
|
}
|
||||||
|
req, err := c.c.NewRequest(ctx, method, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.c.Do(req, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ const (
|
||||||
ReqName = "req"
|
ReqName = "req"
|
||||||
KeyPath = "/" + Version + "/" + KeyName
|
KeyPath = "/" + Version + "/" + KeyName
|
||||||
ReqPath = "/" + Version + "/" + ReqName
|
ReqPath = "/" + Version + "/" + ReqName
|
||||||
|
SSH = "ssh"
|
||||||
|
AccessPath = "/" + Version + "/" + SSH
|
||||||
)
|
)
|
||||||
|
|
||||||
type NewKey struct {
|
type NewKey struct {
|
||||||
|
|
@ -68,6 +70,10 @@ func ApproveURL(id, aid string) string {
|
||||||
return KeyPath + "/" + id + "/" + aid
|
return KeyPath + "/" + id + "/" + aid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AccessURL(host, user, aid string) string {
|
||||||
|
return AccessPath + "/" + host + "/" + user + "/" + aid
|
||||||
|
}
|
||||||
|
|
||||||
type Key struct {
|
type Key struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -170,3 +176,47 @@ func NewApprovals(m types.ApprovalMap) (a Approvals) {
|
||||||
a.Sort()
|
a.Sort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Access struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
User string `json:"user"`
|
||||||
|
AID string `json:"aid"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Status types.Status `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessList []Access
|
||||||
|
|
||||||
|
func (AccessList) Less(a, b Access) int { return cmp.Compare(a.Host, b.Host) }
|
||||||
|
func (a AccessList) Sort() { slices.SortStableFunc(a, a.Less) }
|
||||||
|
func (a AccessList) Len() int { return len(a) }
|
||||||
|
func (a AccessList) Fields() string {
|
||||||
|
return "Host\t User\t Approval ID\t IP\t Status"
|
||||||
|
}
|
||||||
|
func (a AccessList) Values() []string {
|
||||||
|
vals := make([]string, len(a))
|
||||||
|
for i, v := range a {
|
||||||
|
vals[i] = fmt.Sprintf("%s\t %s\t %s\t %s\t %s",
|
||||||
|
v.Host,
|
||||||
|
v.User,
|
||||||
|
v.AID,
|
||||||
|
v.IP,
|
||||||
|
v.Status,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccessList(m types.SSHMap) (a AccessList) {
|
||||||
|
for k, v := range m {
|
||||||
|
a = append(a, Access{
|
||||||
|
Host: v.Host,
|
||||||
|
User: v.User,
|
||||||
|
AID: k,
|
||||||
|
IP: v.IP,
|
||||||
|
Status: v.Status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
a.Sort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,3 +157,57 @@ func reqHandler(ctx *types.Context) {
|
||||||
}
|
}
|
||||||
ctx.OK()
|
ctx.OK()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func accessHandler(ctx *types.Context) {
|
||||||
|
host := ctx.Var("host")
|
||||||
|
user := ctx.Var("user")
|
||||||
|
ip := ctx.ClientIP()
|
||||||
|
ssh := ctx.SSH()
|
||||||
|
aid := ctx.Var("aid")
|
||||||
|
var err error
|
||||||
|
if aid == "" {
|
||||||
|
aid, err = ssh.New(host, user, ip)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Log.Error(err, "error creating access", "host", host, "user", user)
|
||||||
|
ctx.Err(types.ErrTooManyApprovals)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Redirect(schema.AccessURL(host, user, aid), http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := ssh.Status(aid, host, user, ip)
|
||||||
|
switch s {
|
||||||
|
case types.Pending:
|
||||||
|
ctx.Redirect(schema.AccessURL(host, user, aid), http.StatusFound)
|
||||||
|
return
|
||||||
|
case types.Rejected:
|
||||||
|
ctx.Err(types.ErrAccessRequestRejected)
|
||||||
|
return
|
||||||
|
case types.NotFound:
|
||||||
|
ctx.Err(types.ErrAccessReqNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(true)
|
||||||
|
}
|
||||||
|
func accessReqHandler(ctx *types.Context) {
|
||||||
|
id := ctx.Var("id")
|
||||||
|
status := types.Pending
|
||||||
|
switch ctx.Method() {
|
||||||
|
case "PUT":
|
||||||
|
status = types.Approved
|
||||||
|
case "DELETE":
|
||||||
|
status = types.Rejected
|
||||||
|
}
|
||||||
|
err := ctx.SSH().Update(id, status)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Log.Error(err, "error updating ssh request")
|
||||||
|
ctx.Err(types.ErrAccessReqNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.OK()
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessListHandler(ctx *types.Context) {
|
||||||
|
l := ctx.SSH().List()
|
||||||
|
ctx.JSON(schema.NewAccessList(l))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ const (
|
||||||
keyID = schema.KeyPath + core.IDRoute
|
keyID = schema.KeyPath + core.IDRoute
|
||||||
keyReqID = schema.KeyPath + core.IDRoute + core.AIDRoute
|
keyReqID = schema.KeyPath + core.IDRoute + core.AIDRoute
|
||||||
reqID = schema.ReqPath + core.IDRoute
|
reqID = schema.ReqPath + core.IDRoute
|
||||||
|
accessID = schema.AccessPath + core.IDRoute
|
||||||
|
access = schema.AccessPath + core.HostRoute + core.UserRoute
|
||||||
|
accessAID = schema.AccessPath + core.HostRoute + core.UserRoute + core.AIDRoute
|
||||||
)
|
)
|
||||||
|
|
||||||
var Routes = []types.Route{
|
var Routes = []types.Route{
|
||||||
|
|
@ -50,4 +53,25 @@ var Routes = []types.Route{
|
||||||
Handler: local(reqHandler),
|
Handler: local(reqHandler),
|
||||||
Methods: []string{"PUT", "DELETE"},
|
Methods: []string{"PUT", "DELETE"},
|
||||||
},
|
},
|
||||||
|
// SSH
|
||||||
|
{
|
||||||
|
Path: accessID,
|
||||||
|
Handler: local(accessReqHandler),
|
||||||
|
Methods: []string{"PUT", "DELETE"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: schema.AccessPath,
|
||||||
|
Handler: local(accessListHandler),
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: access,
|
||||||
|
Handler: accessHandler,
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: accessAID,
|
||||||
|
Handler: accessHandler,
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ const (
|
||||||
nameReReverse = "[^" + re + "]+"
|
nameReReverse = "[^" + re + "]+"
|
||||||
IDRoute = "/{id}"
|
IDRoute = "/{id}"
|
||||||
AIDRoute = "/{aid}"
|
AIDRoute = "/{aid}"
|
||||||
|
UserRoute = "/{user}"
|
||||||
|
HostRoute = "/{host}"
|
||||||
)
|
)
|
||||||
|
|
||||||
var NameRe = regexp.MustCompile(nameReReverse)
|
var NameRe = regexp.MustCompile(nameReReverse)
|
||||||
|
|
|
||||||
134
pkg/ssh/ssh.go
Normal file
134
pkg/ssh/ssh.go
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
// Copyright (C) 2023 Marius Schellenberger
|
||||||
|
|
||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
apiclient "git.giftfish.de/ston1th/keyctl/pkg/api/client"
|
||||||
|
clientv1 "git.giftfish.de/ston1th/keyctl/pkg/api/v1/client"
|
||||||
|
"git.giftfish.de/ston1th/keyctl/pkg/core"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fs = flag.NewFlagSet("", flag.ExitOnError)
|
||||||
|
|
||||||
|
func exit(err error) {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(arg string) {
|
||||||
|
ctx := context.Background()
|
||||||
|
switch arg {
|
||||||
|
case "approve":
|
||||||
|
id, err := idFlags("access id")
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
c := client()
|
||||||
|
err = c.AcceptAccess(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
case "reject":
|
||||||
|
id, err := idFlags("access id")
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
c := client()
|
||||||
|
err = c.RejectAccess(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
case "login":
|
||||||
|
endpoint, err := loginFlags()
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
opts, err := apiclient.ClientOptionsFromConfig(
|
||||||
|
apiclient.Config{Endpoint: endpoint},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
c := client(opts...)
|
||||||
|
host, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
// TODO: fix me (user)
|
||||||
|
ok, err := c.Access(ctx, host, "user")
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
unix.Exec("/bin/bash", []string{"bash"}, os.Environ()) // does not return
|
||||||
|
}
|
||||||
|
case "req":
|
||||||
|
c := client()
|
||||||
|
access, err := c.GetAccess(ctx)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
print(access)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func client(opts ...apiclient.ClientOption) (c *clientv1.Client) {
|
||||||
|
var err error
|
||||||
|
if opts == nil {
|
||||||
|
opts, err = apiclient.ClientOptionsFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apic := apiclient.NewClient(opts...)
|
||||||
|
//if socket, ok := apic.IsUnix(); ok {
|
||||||
|
// err = unveil(socket)
|
||||||
|
// if err != nil {
|
||||||
|
// exit(err)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
c, err = clientv1.NewClient(apic)
|
||||||
|
if err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func print(tw core.TableWriter) {
|
||||||
|
if tw.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, 1, 0, 1, ' ', tabwriter.Debug)
|
||||||
|
fmt.Fprintln(w, tw.Fields())
|
||||||
|
for _, v := range tw.Values() {
|
||||||
|
fmt.Fprintln(w, v)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func idFlags(desc string) (id string, err error) {
|
||||||
|
fs.StringVar(&id, "id", "", desc)
|
||||||
|
fs.Parse(os.Args[3:])
|
||||||
|
if id == "" {
|
||||||
|
err = errors.New("missing flag: id")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginFlags() (endpoint string, err error) {
|
||||||
|
fs.StringVar(&endpoint, "endpoint", "", "keyctl endpoint")
|
||||||
|
fs.Parse(os.Args[3:])
|
||||||
|
if endpoint == "" {
|
||||||
|
err = errors.New("missing flag: endpoint")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue