"crypto/x509"
"io"
"os"
+ "strings"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.fuhry.dev/runtime/mtls/certutil"
mint_proto "go.fuhry.dev/runtime/proto/service/mint"
"go.fuhry.dev/runtime/utils/context"
+ "go.fuhry.dev/runtime/utils/hostname"
"go.fuhry.dev/runtime/utils/log"
"go.fuhry.dev/runtime/utils/option"
)
return nil, status.Errorf(codes.Unauthenticated, "cannot determine peer certificate")
}
+ if !hostname.ValidHostname.MatchString(req.HostInfo.Hostname) {
+ return nil, status.Errorf(codes.InvalidArgument, "provided hostname %q is invalid", req.HostInfo.Hostname)
+ }
+
+ if !hostname.ValidDomainName.MatchString(req.HostInfo.Domain) {
+ return nil, status.Errorf(codes.InvalidArgument, "provided domain name %q is invalid", req.HostInfo.Domain)
+ }
+
spiffeId := certutil.SpiffeUrlFromCertificate(peerCert)
if spiffeId == nil {
return nil, status.Errorf(codes.Unauthenticated, "cannot find SPIFFE ID in peer certificate")
req.RequestedIdentity)
}
+ template := s.acl.Names(req.RequestedIdentity)
+ kv := map[string]string{
+ "spiffe.domain": remoteId.Domain,
+ "spiffe.class": remoteId.Class.String(),
+ "spiffe.principal": remoteId.Principal,
+ "principal": req.RequestedIdentity,
+ "hostname": req.HostInfo.Hostname,
+ "domain": req.HostInfo.Domain,
+ }
+ for i := range template {
+ for k, v := range kv {
+ template[i] = strings.ReplaceAll(template[i], "{{"+k+"}}", v)
+ }
+ }
+
pub, err := x509.ParsePKIXPublicKey(req.PublicKey)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to parse public key: %v", err)
Principal: req.RequestedIdentity,
}
- csr, sr, err := CreateSignRequest(requestedId, pub)
+ csr, sr, err := CreateSignRequest(requestedId, template, pub)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to create sign request: %v", err)
}
"go.fuhry.dev/runtime/mint/remote_signer"
"go.fuhry.dev/runtime/mtls"
"go.fuhry.dev/runtime/utils/context"
+ "go.fuhry.dev/runtime/utils/hostname"
"go.fuhry.dev/runtime/utils/log"
)
}
}
-func CreateSignRequest(subject *mtls.RemoteIdentity, pub crypto.PublicKey) (*x509.CertificateRequest, *remote_signer.SignRequest, error) {
+func CreateSignRequest(subject *mtls.RemoteIdentity, extraNames []string, pub crypto.PublicKey) (*x509.CertificateRequest, *remote_signer.SignRequest, error) {
var alg x509.PublicKeyAlgorithm
switch pub.(type) {
case *ecdsa.PublicKey:
},
}
+ for _, n := range extraNames {
+ if nUrl, err := url.Parse(n); err == nil && nUrl.Scheme != "" {
+ csrTemplate.URIs = append(csrTemplate.URIs, nUrl)
+ } else if hostname.ValidDomainName.MatchString(n) {
+ csrTemplate.DNSNames = append(csrTemplate.DNSNames, n)
+ }
+ }
+
rs := remote_signer.NewSigner(pub)
_, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, rs)
if !errors.Is(err, remote_signer.ErrSignerNonlocal) {
)
type CertificateRequest struct {
- state protoimpl.MessageState `protogen:"open.v1"`
- RequestedIdentity string `protobuf:"bytes,1,opt,name=requested_identity,json=requestedIdentity,proto3" json:"requested_identity,omitempty"`
- PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ RequestedIdentity string `protobuf:"bytes,1,opt,name=requested_identity,json=requestedIdentity,proto3" json:"requested_identity,omitempty"`
+ PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
+ HostInfo *CertificateRequest_HostInfo `protobuf:"bytes,3,opt,name=host_info,json=hostInfo,proto3" json:"host_info,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
return nil
}
+func (x *CertificateRequest) GetHostInfo() *CertificateRequest_HostInfo {
+ if x != nil {
+ return x.HostInfo
+ }
+ return nil
+}
+
type CertificatePreSignResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
return nil
}
+type CertificateRequest_HostInfo struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
+ Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *CertificateRequest_HostInfo) Reset() {
+ *x = CertificateRequest_HostInfo{}
+ mi := &file_mint_types_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *CertificateRequest_HostInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CertificateRequest_HostInfo) ProtoMessage() {}
+
+func (x *CertificateRequest_HostInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_mint_types_proto_msgTypes[4]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CertificateRequest_HostInfo.ProtoReflect.Descriptor instead.
+func (*CertificateRequest_HostInfo) Descriptor() ([]byte, []int) {
+ return file_mint_types_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *CertificateRequest_HostInfo) GetHostname() string {
+ if x != nil {
+ return x.Hostname
+ }
+ return ""
+}
+
+func (x *CertificateRequest_HostInfo) GetDomain() string {
+ if x != nil {
+ return x.Domain
+ }
+ return ""
+}
+
var File_mint_types_proto protoreflect.FileDescriptor
const file_mint_types_proto_rawDesc = "" +
"\n" +
- "\x10mint_types.proto\x12\x1afuhry.runtime.service.mint\"b\n" +
+ "\x10mint_types.proto\x12\x1afuhry.runtime.service.mint\"\xf8\x01\n" +
"\x12CertificateRequest\x12-\n" +
"\x12requested_identity\x18\x01 \x01(\tR\x11requestedIdentity\x12\x1d\n" +
"\n" +
- "public_key\x18\x02 \x01(\fR\tpublicKey\"H\n" +
+ "public_key\x18\x02 \x01(\fR\tpublicKey\x12T\n" +
+ "\thost_info\x18\x03 \x01(\v27.fuhry.runtime.service.mint.CertificateRequest.HostInfoR\bhostInfo\x1a>\n" +
+ "\bHostInfo\x12\x1a\n" +
+ "\bhostname\x18\x01 \x01(\tR\bhostname\x12\x16\n" +
+ "\x06domain\x18\x02 \x01(\tR\x06domain\"H\n" +
"\x1aCertificatePreSignResponse\x12\x16\n" +
"\x06digest\x18\x01 \x01(\fR\x06digest\x12\x12\n" +
"\x04hash\x18\x02 \x01(\x05R\x04hash\"[\n" +
return file_mint_types_proto_rawDescData
}
-var file_mint_types_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_mint_types_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_mint_types_proto_goTypes = []any{
- (*CertificateRequest)(nil), // 0: fuhry.runtime.service.mint.CertificateRequest
- (*CertificatePreSignResponse)(nil), // 1: fuhry.runtime.service.mint.CertificatePreSignResponse
- (*FinalizeRequest)(nil), // 2: fuhry.runtime.service.mint.FinalizeRequest
- (*CertificateResponse)(nil), // 3: fuhry.runtime.service.mint.CertificateResponse
+ (*CertificateRequest)(nil), // 0: fuhry.runtime.service.mint.CertificateRequest
+ (*CertificatePreSignResponse)(nil), // 1: fuhry.runtime.service.mint.CertificatePreSignResponse
+ (*FinalizeRequest)(nil), // 2: fuhry.runtime.service.mint.FinalizeRequest
+ (*CertificateResponse)(nil), // 3: fuhry.runtime.service.mint.CertificateResponse
+ (*CertificateRequest_HostInfo)(nil), // 4: fuhry.runtime.service.mint.CertificateRequest.HostInfo
}
var file_mint_types_proto_depIdxs = []int32{
- 0, // [0:0] is the sub-list for method output_type
- 0, // [0:0] is the sub-list for method input_type
- 0, // [0:0] is the sub-list for extension type_name
- 0, // [0:0] is the sub-list for extension extendee
- 0, // [0:0] is the sub-list for field type_name
+ 4, // 0: fuhry.runtime.service.mint.CertificateRequest.host_info:type_name -> fuhry.runtime.service.mint.CertificateRequest.HostInfo
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
}
func init() { file_mint_types_proto_init() }
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_mint_types_proto_rawDesc), len(file_mint_types_proto_rawDesc)),
NumEnums: 0,
- NumMessages: 4,
+ NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},