Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use instance name as hostname if not provided #68

Open
david-crespo opened this issue Apr 27, 2021 · 7 comments
Open

Use instance name as hostname if not provided #68

david-crespo opened this issue Apr 27, 2021 · 7 comments
Labels
api Related to the API. mvp
Milestone

Comments

@david-crespo
Copy link
Contributor

Based on the discussion in #66 and elsewhere, in the instance create POST we want to:

  • Make hostname optional (it's currently required)
  • Validate it using the same constraints we're currently using for name
    • If hostname is not provided, set name as hostname

Out of scope for now is worrying about uniqueness of hostname, e.g., if the user creates { name: 'db1', hostname: 'db2' } and then tries to create { name: 'db2' } (which implies a hostname of db2), let them.

@smklein smklein added the api Related to the API. label Sep 1, 2021
@jessfraz jessfraz added the mvp label Jan 24, 2022
@david-crespo david-crespo changed the title Instance hostname validation Use instance name as hostname if not provided Apr 11, 2022
@david-crespo
Copy link
Contributor Author

Right now hostname is required but accepts empty string.

pub struct InstanceCreate {
#[serde(flatten)]
pub identity: IdentityMetadataCreateParams,
pub ncpus: InstanceCpuCount,
pub memory: ByteCount,
pub hostname: String, // TODO-cleanup different type?

@zephraph
Copy link
Contributor

Some questions here...

  • If a user decides not to create a NIC for an instance, should they even be given the option to input a hostname?
  • If there are multiple NICs, which is the hostname associated to? (This is more curiosity than anything)

@rzezeski
Copy link
Contributor

rzezeski commented Apr 12, 2022

If a user decides not to create a NIC for an instance, should they even be given the option to input a hostname?

If my computer has no NIC/plumbed interface it still has a hostname.

If there are multiple NICs, which is the hostname associated to? (This is more curiosity than anything)

The internal DNS for an instance resolves to its primary IPv4/IPv6 interfaces. This is discussed in RFD 21 §2.7.2 and §2.10.4.

@david-crespo
Copy link
Contributor Author

david-crespo commented Apr 12, 2022

Right now the API lets you create an instance without any network interfaces. Would someone actually want to do that? Might we instead validate that there is one at create time, or always create a default if none is provided as a create-time param?

Related question: if an instance is created with no interfaces, it can't have DNS records as there is no IP address to resolve to, correct?

Google prefills one:

image

and doesn't let you create the instance if you delete it:

image

With AWS it's harder to tell — it seems like they just create a network interface for you no matter what. The create instance web UI doesn't even let you explicitly configure network interfaces, only VPCs and subnets, which probably get an interface by default.

[EC2-VPC] All instances have a network interface with a primary private IPv4 address. If you don't specify this address, we choose one from the IPv4 range of your subnet.

https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html

@rzezeski
Copy link
Contributor

EDIT: As I'm finishing up this comment I now realize that it's kind of getting away from the scope of the initial issue. This seems like a thing to potentially bring up in CP huddle today.

I feel like the text in RFD 6 §2.2.1 indicates that if no network interface is specified it will default to adding one under the default subnet.

        interfaces:
            description: |
                An array of network configurations for this instance. These
                specify how interfaces are configured to interact with other
                network services, such as connecting to the internet.
                Multiple interfaces are supported per instance. If none are
                given at creation time, the default subnetwork is added.

My inclination is to always provide a default interface if the user does not provide one.

But to play devil's advocate: if an instance was created with no network interface, the only way to access it would be the serial console (or I guess VNC/RDP too). Once on the console the user would not be able to reach the internet or even any other instance in that VPC. So the guest image would have to be self contained for whatever the user wants to do. Is there a use case for this? Maybe for some type of preparation/validation before the guest ever sees a single network packet? It feels like a rare use case (also there are other ways to achieve this both from inside and from outside of the guest). That said, perhaps the default should be to provide a single default interface (if one isn't specific explicitly), but also have an option to stand up the instance with no interface. Like an explicit flag/checkbox that says "hey no, I really want no interface on this guest".

However, I think something like this also has to take into account the ramifications that has up and down our stack. My mental model up to this point has been minimum one interface on every guest. Other people working on other parts of the stack may have a different mental model and thus codifying other constraints in the system. Also, it adds another branch/path to our overall set of possible state spaces; one that may not be exercised frequently and could come to bite us. It would be good to get clarity on this and make a decision.

@rmustacc
Copy link

I have a bunch of different thoughts on this. Let me try to address a couple of different things around instance name, hostname, and the question of should we support no interfaces being allowed.

I want to call out a caveat that I'm trying to define the terms as I use them. I will try to be consistent and these may overlap with common convention, but may not quite overlap with some of the most technically correct definitions, let's try not to focus on that too much as opposed to the concepts.

Role of Naming

First, I think it's worth going through and talking about the different names that we have for an instance. There are a couple of different of classes of how names are used that are worth calling out:

  1. There are names for an instance in the API name space. Depending on your perspective, we have two names here, a string and a uuid, in two different name spaces. Let's come back to that string in a moment.
  2. There is a notion of what name you see inside an instance. From a user perspective this may be the name that you see at a shell prompt or is used in an /etc/hosts file as an alias for a localhost.
  3. There are names that are discoverable in an external name service system (like DNS) that resolve to an IP address, which in turn is used to point to an instance.

Instance Naming

I think it's worth talking about what we want out of instance naming long term. While today we're constraining the character set that we allow for instance naming, I think our restriction to effectively an ASCII character set is a mistake. As we go broader out in the market and want to provide more diveristy from a product capability persepctive, it seems like allowing UTF-8 based naming like fëanor, 光, or whatever language that folks actually use is something that we will want to do.

To be clear, this path comes with a lot of complications:

  • We have issues around normalization and some of the very real challenges of Homograph attacks
  • Names involving unicode character points create challenges for naming in the use cases of (2) and (3) above. In some cases they are invalid.

While this isn't something we want for the MVP, I think it's worth considering as we further discuss this, hostnames, and why these became at one point separate fields.

Hostnames, Nodenames, nsswitch, and you

Let's talk about what's happening inside an instance for a moment. OS often want a canonical name that they use. For users, this is what actually shows up in your shell through what your shell calls the "hostname"; however, there is nothing about this name that requires it to be in DNS at all.

In fact, the question of where do systems even look up names and where do these names, on *nix based systems, is actaully much more than DNS. There are many things here that provided name look ups in the past (and some are still used today): NIS, LDAP, a file, etc. These sources of names allow for a system to have multiple names that resolve to a way to reach the instance (often an IP address).

In some systems, the canonical name of an instance is called a node name as to distinguish it from something that shows up in DNS, etc. Think of your own use of say Linux, Windows, or macOS. In many cases a device has a name, but that name may not correspond to DNS at all. My laptop for example may think it's name is 'elbereth'; however, there is no actual DNS name that ever resolves to this.

Now, you may say, so what, why do we care. However, it's actually the case that other parts of a subsystem often use this. Whenever we start getting into the darker parts of the operating system, such as libnsl and the old Sun/ONC RPC which is then used by NFS and others. Each OS is a bit different in how they get this; however, the important part of all this is that it is not necessarily what you'll find in DNS ever.

In fact, whether DNS, LDAP, /etc/hosts, or something else, there are a lot of different names that can refer to an IP address. When we say "hostname", this is what I actually often think of as distinct from the node name.

So why go into all this, well the main thing is that what we're trying to accomplish with naming use case (2) is really more like a node name here and therefore is something we'd want to show up regardless of network interfaces we have in an instance or not. Even if you don't have an active IP address on a laptop, you still have a name.

However, these distinctions aren't what most people expect and what we have tried to propose is somewhat of a single DNS based name for an instance, which is case (3). And by convention the more that (1), (2), and (3) match the better.

Evolution of the hostname field

The evolution of the hostname field based on my fragmented memory is as follows. At first, we were referring to a lot of things in the API by UUIDs and not by names. However, there was a push at some point to make this done by names. There was also a case where folks wanted to be able to change the names of instances, this made a lot of sense. Just imagine you are renaming things because you either made a typo or now you want to append some additional information. No one really wants to recreate all that.

However, this also then gets to the complication of the overlap between uses (1) and (3). You may want to change a DNS name or not have it change based on the above. The most important observation is that changing the name could cause a service disruption if it inherently went its way and changed what's in DNS (if this was unexpected). And basically that in the spirit of giving users flexibility and having to recreate on deletes, this leads to this kind of thing.

All of this led to a proposal of a policy where hostname is seeded from the instance name field (1) if it is not provided. This would then be used to satisfy (2) / (3).

There are several places in the API where we have separately done a distinction with name in the form of "name" and "DNS Name". This mostly shows up in the network APIs as here we have a similar path of seeding the DNS name for an API object. In this case, these influence often what is part of the domain name per RFD 21. However, there's an interesting distinction here, the floating IP actually uses the DNS name as the leading part ala the instance hostname. This is because instances use <instance>.<az>.inst and floating IPs are <name>.fip. This led to the broader future looking use in DNS of these things:

<instance>.<az>.inst.<vpc>.<project>.<org>.<suffix>
<name>.fip.<vpc>.<project>.<org>.<suffix>
<service>.svc.<vpc>.<project>.<org>.<suffix>
<tag>.tag.<vpc>.<project>.<org>.<suffix>
<load balancer>.lb.<vpc>.<project>.<org>.<suffix>

The thing that really does make instances unique is the fact that we're using one field for (2) and (3), where as everything else in networking uses "DNS name" strictly for (3).

Instances without Networking

An important question that's also here is should we support instances without any networking interfaces. There are a couple of use cases that originally motivated this a bit. We'll first introduce them then discuss alternatives:

  • At some point we will want to support moving an instance from one VPC Subnet to another. Being able to first delete a NIC and then add a new one is something that'd be useful. That is passing through a state where there are no instances.
    • This could be addressed by having a move API; however, that adds a bit more surface area in the API. On the other hand, if we end up exploring more transactional APIs in the future, then this may fall out there.
    • We could simply require that you always have one interface and just let users deal with it being on two different ones. This doesn't seem that bad, even if I don't love it.
  • Another use case that I originally had was just using an offline instance that could only be accessed via serial/vnc. The bad idea here was something that had some kind of shared online, but not network-accessible secret. This isn't really a very good one.
  • A second version of the above was actually someone wanting to isolate an instance for forensic purposes.
    • A straightforward counter to this is to just use VPC firewall rules or similar to drop all traffic. This certainly works and may be a better path, though I worry about error. Note, this would also work for the case before this.

Caveat Emptor: Metadata

One challenge with no network interfaces is it would mean that one wouldn't be able to access dynamic metadata over HTTP. On the other hand, there may be reasons we prefer to ignore this, e.g. to allow for some restrictions in-instance about what can access this (based on the assumption of either using FS permissions or something more so like viosock that can have some additional restrictions).

Rambling Summary

So I guess the summary of the above is:

  • Originally we were planning to seed the hostname from the instance name.
  • Instance "hostname" is awkward and can lead to confusion due to conflation of both naming cases (2) or (3).
  • I think it may make sense to allow for no interfaces; however, it's also something we can come back to.
  • Someday it may be nice to support UTF-8 in API names for case (1).

@smklein smklein added this to the MVP milestone Jan 20, 2023
@david-crespo
Copy link
Contributor Author

david-crespo commented May 16, 2023

The point about wanting to change the name of the instance without it changing the hostname other things may be pointing at seems to me the strongest point in favor of keeping hostname separate. However, we'd have to do some work we're not currently doing (I think) to enforce the uniqueness of the hostname. So I think my vote is to eliminate the hostname field for now and use name where it's currently used (passed to cloud-init) and where it's going to be used soon (DNS).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api Related to the API. mvp
Projects
None yet
Development

No branches or pull requests

6 participants