Skip to content

Commit

Permalink
Add TLSHostname field to controller model (#1225)
Browse files Browse the repository at this point in the history
* Add TLSHostname field to controller model

* Added tests

* Reduce log level on isAdmin check

* Tweak error message
  • Loading branch information
kian99 authored Jun 3, 2024
1 parent c1cc873 commit a365444
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 7 deletions.
3 changes: 3 additions & 0 deletions api/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type AddControllerRequest struct {
// themselves are migrated.
PublicAddress string `json:"public-address,omitempty"`

// TLSHostname is the hostname used for TLS verification.
TLSHostname string `json:"tls-hostname,omitempty"`

// APIAddresses contains the currently known API addresses for the
// controller.
APIAddresses []string `json:"api-addresses,omitempty"`
Expand Down
9 changes: 6 additions & 3 deletions cmd/jimmctl/cmd/controllerinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type controllerInfoCommand struct {
publicAddress string
file cmd.FileVar
local bool
tlsHostname string
}

func (c *controllerInfoCommand) Info() *cmd.Info {
Expand All @@ -66,6 +67,7 @@ func (c *controllerInfoCommand) Info() *cmd.Info {
func (c *controllerInfoCommand) SetFlags(f *gnuflag.FlagSet) {
c.CommandBase.SetFlags(f)
f.BoolVar(&c.local, "local", false, "If local flag is specified, then the local API address and CA cert of the controller will be used.")
f.StringVar(&c.tlsHostname, "tls-hostname", "", "Specify the hostname for TLS verfiication.")
}

// Init implements the cmd.Command interface.
Expand All @@ -80,6 +82,9 @@ func (c *controllerInfoCommand) Init(args []string) error {
if len(args) > 3 {
return errors.New("too many args")
}
if c.local && len(c.publicAddress) > 0 {
return errors.New("cannot set both public address and local flag")
}
return nil
}

Expand All @@ -103,6 +108,7 @@ func (c *controllerInfoCommand) Run(ctxt *cmd.Context) error {
Password: accountDetails.Password,
}

info.TLSHostname = c.tlsHostname
info.PublicAddress = c.publicAddress
if c.local {
info.PublicAddress = controller.APIEndpoints[0]
Expand All @@ -111,9 +117,6 @@ func (c *controllerInfoCommand) Run(ctxt *cmd.Context) error {
if info.PublicAddress == "" {
return errors.New("public address must be set")
}
if c.local && len(c.publicAddress) > 0 {
return errors.New("please do not set both the address argument and the local flag")
}

data, err := yaml.Marshal(info)
if err != nil {
Expand Down
59 changes: 58 additions & 1 deletion cmd/jimmctl/cmd/controllerinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,5 +244,62 @@ func (s *controllerInfoSuite) TestControllerInfoCannotProvideAddrAndLocalFlag(c
fname := path.Join(dir, "test.yaml")

_, err = cmdtesting.RunCommand(c, cmd.NewControllerInfoCommandForTesting(store), "controller-1", fname, "myaddress", "--local")
c.Assert(err, gc.ErrorMatches, "please do not set both the address argument and the local flag")
c.Assert(err, gc.ErrorMatches, "cannot set both public address and local flag")
}

func (s *controllerInfoSuite) TestControllerInfoWithTlsFlag(c *gc.C) {
store := s.ClientStore()
store.Controllers["controller-1"] = jujuclient.ControllerDetails{
ControllerUUID: "982b16d9-a945-4762-b684-fd4fd885aa11",
APIEndpoints: []string{"127.0.0.1:17070"},
PublicDNSName: "controller1.example.com",
CACert: `-----BEGIN CERTIFICATE-----
MIID/jCCAmagAwIBAgIVANxsMrzsXrdpjjUoxWQm1RCkmWcqMA0GCSqGSIb3DQEB
CwUAMCYxDTALBgNVBAoTBEp1anUxFTATBgNVBAMTDGp1anUgdGVzdGluZzAeFw0y
MDA0MDgwNTI3NTBaFw0zMDA0MDgwNTMyNTBaMCYxDTALBgNVBAoTBEp1anUxFTAT
BgNVBAMTDGp1anUgdGVzdGluZzCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoC
ggGBAOW4k2bmXXU3tJ8H5AsGkp8ENLJXzU4SCOCB+X0jPQRVpFtywBVD96z+l+qW
ndGLIg5zMQTtZm71CaOw+8Sl03XU0f28Xrjf+FZCAPID1c7NBttUShbu84euFoCS
C8yobj6JzLz7QswvkshYQ7JEZ88UXtVHqg6MGYFdu+cX/dE1jC7aHg9bus/P6bFH
PVFcHVVxNbLy98Id1iB7i0s97H17nu9O7ZKMrAQAX6dfAELAFQVicdN3WpfwNXEj
M2KIrqttpM8s6/57mi9UJFYGdAEDNkJr/dI506VdGLpiqTFhQK6ztfDfY08QbWk6
iJn8vzWvNW8WthmBtEDpv+DL+a5SJSLSAIZn9sbuBBpiX+csZb66fYhKFFIUrIa5
lrjw8yiHJ4kgsEZJSYaAn7guqmOv8clvy1E2JjsOfGycest6+1/mNdMRFgrMxdzD
0M2yZ96zrdfF/QXpi7Hk7jFLzimuujNUpKFv7k+XObQFxeXnoFrYVkj3YT8hhYF0
mGRkAwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAN
BgkqhkiG9w0BAQsFAAOCAYEAd7GrziPRmjoK3HDF10S+5NgoKYvkOuk2jDap2Qaq
ZFjDvrDA2tr6U0FGY+Hz+QfvtgT+YpJB5IvABvSXdq37llwKGsiSOZSrpHyTsOB0
VcZAF3GMk1nHYMr0o1xRV2gm/ax+vUEStj0k2gTs/p57uhKcCUXR0p3PWXKcRj9a
nVf5bdVkt6ghGs7/uEvj303raUFSf5dJ4C9RTgBK2E9/wlBYNyj5vcsshNpz8kt6
RuARM6Boq2EwKkpRlbvImDM8ZJJLwtpijYrx3egfOSEA7Wfwgwn+B359XcosOee5
n5BC62EjaP85cM9HCtp2DwKjNSosxja723qZPY6Z0Y7IVn3JVjgC2kWP6GViwb+v
l9uwx9ASHPz9ilh6gpjgIifOKZYCaBSS9g8VxHpO5Izxj4vi4AX5cebDg3SzDVik
hZuWHpuOT120okoutwuUSU9448cXLGZfoCZjjdMKXmOj8EEec1diDP4mhegYGezD
LQRNNlaY2ajLt0paowf/Xxb8
-----END CERTIFICATE-----`,
}
store.Accounts["controller-1"] = jujuclient.AccountDetails{
User: "test-user",
Password: "super-secret-password",
}
dir, err := os.MkdirTemp("", "controller-info-test")
c.Assert(err, gc.Equals, nil)
defer os.RemoveAll(dir)

fname := path.Join(dir, "test.yaml")

_, err = cmdtesting.RunCommand(c, cmd.NewControllerInfoCommandForTesting(store), "controller-1", fname, "myaddress", "--tls-hostname", "foo")
c.Assert(err, gc.IsNil)

data, err := os.ReadFile(fname)
c.Assert(err, gc.IsNil)
c.Assert(string(data), gc.Matches, `api-addresses:
- 127.0.0.1:17070
name: controller-1
password: super-secret-password
public-address: myaddress
tls-hostname: foo
username: test-user
uuid: 982b16d9-a945-4762-b684-fd4fd885aa11
`)
}
4 changes: 4 additions & 0 deletions internal/dbmodel/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ type Controller struct {
// name and port.
PublicAddress string

// TLSHostname provides a hostname that will be used for TLS verfication.
// Useful for local dev to avoid TLS issues.
TLSHostname string `gorm:"column:tls_hostname"`

// CloudName is the name of the cloud which is hosting this
// controller.
CloudName string
Expand Down
4 changes: 4 additions & 0 deletions internal/dbmodel/sql/postgres/1_8.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- 1_8.sql is a migration that adds a tls_hostname column to the controller table.
ALTER TABLE controllers ADD COLUMN tls_hostname TEXT;

UPDATE versions SET major=1, minor=8 WHERE component='jimmdb';
2 changes: 1 addition & 1 deletion internal/dbmodel/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
// Minor is the minor version of the model described in the dbmodel
// package. It should be incremented for any change made to the
// database model from database model in a released JIMM.
Minor = 7
Minor = 8
)

type Version struct {
Expand Down
1 change: 1 addition & 0 deletions internal/jujuapi/jimm.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func (r *controllerRoot) AddController(ctx context.Context, req apiparams.AddCon
CACertificate: req.CACertificate,
AdminIdentityName: req.Username,
AdminPassword: req.Password,
TLSHostname: req.TLSHostname,
}
nphps, err := network.ParseProviderHostPorts(req.APIAddresses...)
if err != nil {
Expand Down
37 changes: 37 additions & 0 deletions internal/jujuapi/jimm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,43 @@ func (s *jimmSuite) TestAddController(c *gc.C) {
c.Assert(jujuparams.IsBadRequest(err), gc.Equals, true)
}

func (s *jimmSuite) TestAddControllerCustomTLSHostname(c *gc.C) {
conn := s.open(c, nil, "alice")
defer conn.Close()
client := api.NewClient(conn)

info := s.APIInfo(c)

acr := apiparams.AddControllerRequest{
UUID: info.ControllerUUID,
Name: "controller-2",
APIAddresses: info.Addrs,
CACertificate: info.CACert,
Username: info.Tag.Id(),
Password: info.Password,
TLSHostname: "foo",
}

_, err := client.AddController(&acr)
c.Assert(err, gc.ErrorMatches, "failed to dial the controller")
acr.TLSHostname = "juju-apiserver"
ci, err := client.AddController(&acr)
c.Assert(err, gc.IsNil)
c.Assert(ci, jc.DeepEquals, apiparams.ControllerInfo{
Name: "controller-2",
UUID: info.ControllerUUID,
APIAddresses: info.Addrs,
CACertificate: info.CACert,
CloudTag: names.NewCloudTag(jimmtest.TestCloudName).String(),
CloudRegion: jimmtest.TestCloudRegionName,
AgentVersion: s.Model.Controller.AgentVersion,
Status: jujuparams.EntityStatus{
Status: "available",
},
})

}

func (s *jimmSuite) TestRemoveController(c *gc.C) {
conn := s.open(c, nil, "alice")
defer conn.Close()
Expand Down
2 changes: 1 addition & 1 deletion internal/openfga/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func IsAdministrator[T administratorT](ctx context.Context, u *User, resource T)
return false, errors.E(err)
}
if isAdmin {
zapctx.Info(
zapctx.Debug(
ctx,
"user is resource administrator",
zap.String("user", u.Tag().String()),
Expand Down
3 changes: 2 additions & 1 deletion internal/rpc/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ func Dial(ctx context.Context, ctl *dbmodel.Controller, modelTag names.ModelTag,
zapctx.Warn(ctx, "no CA certificates added")
}
tlsConfig = &tls.Config{
RootCAs: cp,
RootCAs: cp,
ServerName: ctl.TLSHostname,
}
}
dialer := Dialer{
Expand Down

0 comments on commit a365444

Please sign in to comment.