Skip to content

Commit b59dbc5

Browse files
Merge pull request #83 from Infisical/feat-pki/PKI-52
feat: add certificates to Infisical agent
2 parents 5c9d7e3 + 4b2deeb commit b59dbc5

File tree

4 files changed

+1905
-154
lines changed

4 files changed

+1905
-154
lines changed

certificate-agent-config.yaml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
version: v1
2+
3+
infisical:
4+
address: "https://app.infisical.com/"
5+
6+
auth:
7+
type: "universal-auth"
8+
config:
9+
client-id: "./client-id"
10+
client-secret: "./client-secret"
11+
remove_client_secret_on_read: false
12+
13+
certificates:
14+
- profile-name: "my-profile-name"
15+
project-slug: "my-project-slug"
16+
17+
# Certificate parameters
18+
attributes:
19+
common-name: "api.mycompany.com"
20+
alt-names:
21+
- "www.api.mycompany.com"
22+
- "internal-api.mycompany.com"
23+
key-algorithm: "RSA_2048"
24+
signature-algorithm: "RSA-SHA256"
25+
key-usages:
26+
- "digital_signature"
27+
- "key_encipherment"
28+
extended-key-usages:
29+
- "server_auth"
30+
ttl: "30d"
31+
lifecycle:
32+
renew-before-expiry: "1d" # When to start checking for renewal before expiration
33+
status-check-interval: "6h" # How often to check certificate status and renewal needs
34+
35+
# Post-hooks for automation
36+
post-hooks:
37+
on-issuance:
38+
command: "systemctl reload nginx"
39+
timeout: 30
40+
on-renewal:
41+
command: "systemctl reload nginx"
42+
timeout: 30
43+
on-failure:
44+
command: "logger 'Certificate failed for api.mycompany.com'"
45+
timeout: 10
46+
47+
file-output:
48+
private-key-path: "./certs/web-server/private.key"
49+
certificate-path: "./certs/web-server/certificate.crt"
50+
certificate-chain-path: "./certs/web-server/chain.crt"
51+
file-permissions: "0600"
52+
directory-permissions: "0755"

packages/api/api.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ const (
5555
operationCallPAMSessionTermination = "CallPAMSessionTermination"
5656
operationCallOrgRelayHeartBeat = "CallOrgRelayHeartBeat"
5757
operationCallInstanceRelayHeartBeat = "CallInstanceRelayHeartBeat"
58+
operationCallIssueCertificate = "CallIssueCertificate"
59+
operationCallRetrieveCertificate = "CallRetrieveCertificate"
60+
operationCallRenewCertificate = "CallRenewCertificate"
61+
operationCallGetCertificateRequest = "CallGetCertificateRequest"
5862
)
5963

6064
var ErrNotFound = errors.New("resource not found")
@@ -291,6 +295,45 @@ func CallGetProjectById(httpClient *resty.Client, id string) (Project, error) {
291295
return projectResponse.Project, nil
292296
}
293297

298+
func CallGetProjectBySlug(httpClient *resty.Client, slug string) (Project, error) {
299+
var projectResponse GetProjectBySlugResponse
300+
response, err := httpClient.
301+
R().
302+
SetResult(&projectResponse).
303+
SetHeader("User-Agent", USER_AGENT).
304+
Get(fmt.Sprintf("%v/v1/projects/slug/%s", config.INFISICAL_URL, slug))
305+
306+
if err != nil {
307+
return Project{}, NewGenericRequestError("CallGetProjectBySlug", err)
308+
}
309+
310+
if response.IsError() {
311+
return Project{}, NewAPIErrorWithResponse("CallGetProjectBySlug", response, nil)
312+
}
313+
314+
return Project(projectResponse), nil
315+
}
316+
317+
func CallGetCertificateProfileBySlug(httpClient *resty.Client, projectId, slug string) (CertificateProfile, error) {
318+
var profileResponse GetCertificateProfileResponse
319+
response, err := httpClient.
320+
R().
321+
SetResult(&profileResponse).
322+
SetHeader("User-Agent", USER_AGENT).
323+
SetQueryParam("projectId", projectId).
324+
Get(fmt.Sprintf("%v/v1/cert-manager/certificate-profiles/slug/%s", config.INFISICAL_URL, slug))
325+
326+
if err != nil {
327+
return CertificateProfile{}, NewGenericRequestError("CallGetCertificateProfileBySlug", err)
328+
}
329+
330+
if response.IsError() {
331+
return CertificateProfile{}, NewAPIErrorWithResponse("CallGetCertificateProfileBySlug", response, nil)
332+
}
333+
334+
return profileResponse.CertificateProfile, nil
335+
}
336+
294337
func CallIsAuthenticated(httpClient *resty.Client) bool {
295338
var workSpacesResponse GetWorkSpacesResponse
296339
response, err := httpClient.
@@ -956,3 +999,81 @@ func CallPAMSessionTermination(httpClient *resty.Client, sessionId string) error
956999

9571000
return nil
9581001
}
1002+
1003+
func CallIssueCertificate(httpClient *resty.Client, request IssueCertificateRequest) (*CertificateResponse, error) {
1004+
var resBody CertificateResponse
1005+
response, err := httpClient.
1006+
R().
1007+
SetResult(&resBody).
1008+
SetHeader("User-Agent", USER_AGENT).
1009+
SetBody(request).
1010+
Post(fmt.Sprintf("%v/v1/cert-manager/certificates", config.INFISICAL_URL))
1011+
1012+
if err != nil {
1013+
return nil, NewGenericRequestError(operationCallIssueCertificate, err)
1014+
}
1015+
1016+
if response.IsError() {
1017+
return nil, NewAPIErrorWithResponse(operationCallIssueCertificate, response, nil)
1018+
}
1019+
1020+
return &resBody, nil
1021+
}
1022+
1023+
func CallRetrieveCertificate(httpClient *resty.Client, certificateId string) (*RetrieveCertificateResponse, error) {
1024+
var resBody RetrieveCertificateResponse
1025+
response, err := httpClient.
1026+
R().
1027+
SetResult(&resBody).
1028+
SetHeader("User-Agent", USER_AGENT).
1029+
Get(fmt.Sprintf("%v/v1/cert-manager/certificates/%s", config.INFISICAL_URL, certificateId))
1030+
1031+
if err != nil {
1032+
return nil, NewGenericRequestError(operationCallRetrieveCertificate, err)
1033+
}
1034+
1035+
if response.IsError() {
1036+
return nil, NewAPIErrorWithResponse(operationCallRetrieveCertificate, response, nil)
1037+
}
1038+
1039+
return &resBody, nil
1040+
}
1041+
1042+
func CallRenewCertificate(httpClient *resty.Client, certificateId string, request RenewCertificateRequest) (*RenewCertificateResponse, error) {
1043+
var resBody RenewCertificateResponse
1044+
response, err := httpClient.
1045+
R().
1046+
SetResult(&resBody).
1047+
SetHeader("User-Agent", USER_AGENT).
1048+
SetBody(request).
1049+
Post(fmt.Sprintf("%v/v1/cert-manager/certificates/%s/renew", config.INFISICAL_URL, certificateId))
1050+
1051+
if err != nil {
1052+
return nil, NewGenericRequestError(operationCallRenewCertificate, err)
1053+
}
1054+
1055+
if response.IsError() {
1056+
return nil, NewAPIErrorWithResponse(operationCallRenewCertificate, response, nil)
1057+
}
1058+
1059+
return &resBody, nil
1060+
}
1061+
1062+
func CallGetCertificateRequest(httpClient *resty.Client, certificateRequestId string) (*GetCertificateRequestResponse, error) {
1063+
var resBody GetCertificateRequestResponse
1064+
response, err := httpClient.
1065+
R().
1066+
SetResult(&resBody).
1067+
SetHeader("User-Agent", USER_AGENT).
1068+
Get(fmt.Sprintf("%v/v1/cert-manager/certificates/certificate-requests/%s", config.INFISICAL_URL, certificateRequestId))
1069+
1070+
if err != nil {
1071+
return nil, NewGenericRequestError(operationCallGetCertificateRequest, err)
1072+
}
1073+
1074+
if response.IsError() {
1075+
return nil, NewAPIErrorWithResponse(operationCallGetCertificateRequest, response, nil)
1076+
}
1077+
1078+
return &resBody, nil
1079+
}

packages/api/model.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,21 @@ type GetProjectByIdResponse struct {
136136
Project Project `json:"workspace"`
137137
}
138138

139+
type GetProjectBySlugResponse Project
140+
141+
type CertificateProfile struct {
142+
ID string `json:"id"`
143+
Name string `json:"name"`
144+
Description string `json:"description"`
145+
ProjectID string `json:"projectId"`
146+
CaID string `json:"caId"`
147+
CertificateTemplateID string `json:"certificateTemplateId"`
148+
}
149+
150+
type GetCertificateProfileResponse struct {
151+
CertificateProfile CertificateProfile `json:"certificateProfile"`
152+
}
153+
139154
type GetOrganizationsResponse struct {
140155
Organizations []struct {
141156
ID string `json:"id"`
@@ -859,3 +874,90 @@ type UploadPAMSessionLogsRequest struct {
859874
type RelayHeartbeatRequest struct {
860875
Name string `json:"name"`
861876
}
877+
878+
type AltName struct {
879+
Type string `json:"type"`
880+
Value string `json:"value"`
881+
}
882+
883+
type CertificateAttributes struct {
884+
TTL string `json:"ttl,omitempty"`
885+
SignatureAlgorithm string `json:"signatureAlgorithm,omitempty"`
886+
KeyAlgorithm string `json:"keyAlgorithm,omitempty"`
887+
CommonName string `json:"commonName,omitempty"`
888+
KeyUsages []string `json:"keyUsages,omitempty"`
889+
ExtendedKeyUsages []string `json:"extendedKeyUsages,omitempty"`
890+
NotBefore string `json:"notBefore,omitempty"`
891+
NotAfter string `json:"notAfter,omitempty"`
892+
AltNames []AltName `json:"altNames,omitempty"`
893+
RemoveRootsFromChain bool `json:"removeRootsFromChain,omitempty"`
894+
}
895+
896+
type IssueCertificateRequest struct {
897+
ProfileID string `json:"profileId"`
898+
CSR string `json:"csr,omitempty"`
899+
Attributes *CertificateAttributes `json:"attributes,omitempty"`
900+
}
901+
902+
type CertificateData struct {
903+
Certificate string `json:"certificate"`
904+
IssuingCaCertificate string `json:"issuingCaCertificate"`
905+
CertificateChain string `json:"certificateChain"`
906+
PrivateKey string `json:"privateKey,omitempty"`
907+
SerialNumber string `json:"serialNumber"`
908+
CertificateID string `json:"certificateId"`
909+
}
910+
911+
type CertificateResponse struct {
912+
Certificate *CertificateData `json:"certificate,omitempty"`
913+
CertificateRequestID string `json:"certificateRequestId"`
914+
}
915+
916+
type RetrieveCertificateResponse struct {
917+
Certificate struct {
918+
ID string `json:"id"`
919+
CreatedAt time.Time `json:"createdAt"`
920+
UpdatedAt time.Time `json:"updatedAt"`
921+
Status string `json:"status"`
922+
SerialNumber string `json:"serialNumber"`
923+
CommonName string `json:"commonName"`
924+
NotBefore time.Time `json:"notBefore"`
925+
NotAfter time.Time `json:"notAfter"`
926+
ProjectId string `json:"projectId"`
927+
CaId string `json:"caId"`
928+
KeyUsages []string `json:"keyUsages"`
929+
ExtendedKeyUsages []string `json:"extendedKeyUsages"`
930+
Certificate string `json:"certificate,omitempty"`
931+
CertificateChain string `json:"certificateChain,omitempty"`
932+
PrivateKey string `json:"privateKey,omitempty"`
933+
} `json:"certificate"`
934+
}
935+
936+
type RenewCertificateRequest struct {
937+
}
938+
939+
type RenewCertificateResponse struct {
940+
Certificate string `json:"certificate"`
941+
IssuingCaCertificate string `json:"issuingCaCertificate"`
942+
CertificateChain string `json:"certificateChain"`
943+
PrivateKey string `json:"privateKey"`
944+
SerialNumber string `json:"serialNumber"`
945+
CertificateID string `json:"certificateId"`
946+
CertificateRequestID string `json:"certificateRequestId,omitempty"`
947+
}
948+
949+
type GetCertificateRequestResponse struct {
950+
Status string `json:"status"` // "pending", "issued", "failed"
951+
CreatedAt time.Time `json:"createdAt"`
952+
UpdatedAt time.Time `json:"updatedAt"`
953+
CommonName string `json:"commonName,omitempty"`
954+
ProjectID string `json:"projectId,omitempty"`
955+
ProfileID string `json:"profileId,omitempty"`
956+
Certificate *string `json:"certificate,omitempty"`
957+
IssuingCaCertificate *string `json:"issuingCaCertificate,omitempty"`
958+
CertificateChain *string `json:"certificateChain,omitempty"`
959+
PrivateKey *string `json:"privateKey,omitempty"`
960+
SerialNumber *string `json:"serialNumber,omitempty"`
961+
CertificateID *string `json:"certificateId,omitempty"`
962+
ErrorMessage *string `json:"errorMessage,omitempty"`
963+
}

0 commit comments

Comments
 (0)