@@ -89,13 +89,13 @@ type RetryConfig struct {
8989}
9090
9191type Config struct {
92- CertificateManagement string `yaml:"certificate-management ,omitempty"`
93- Infisical InfisicalConfig `yaml:"infisical"`
94- Auth AuthConfig `yaml:"auth"`
95- Sinks []Sink `yaml:"sinks"`
96- Cache CacheConfig `yaml:"cache,omitempty"`
97- Templates []Template `yaml:"templates"`
98- Certificates []AgentCertificateConfig `yaml:"certificates,omitempty"`
92+ Version string `yaml:"version ,omitempty"`
93+ Infisical InfisicalConfig `yaml:"infisical"`
94+ Auth AuthConfig `yaml:"auth"`
95+ Sinks []Sink `yaml:"sinks"`
96+ Cache CacheConfig `yaml:"cache,omitempty"`
97+ Templates []Template `yaml:"templates"`
98+ Certificates []AgentCertificateConfig `yaml:"certificates,omitempty"`
9999}
100100
101101type TemplateWithID struct {
@@ -243,7 +243,7 @@ type AgentCertificateConfig struct {
243243 CertificateChainPath string `yaml:"certificate-chain-path,omitempty"`
244244 FilePermissions string `yaml:"file-permissions,omitempty"`
245245 DirectoryPermissions string `yaml:"directory-permissions,omitempty"`
246- } `yaml:"output- file-configuration ,omitempty"`
246+ } `yaml:"file-output ,omitempty"`
247247}
248248
249249type DynamicSecretLeaseWithTTL struct {
@@ -777,35 +777,49 @@ func ParseAuthConfig(authConfigFile []byte, destination interface{}) error {
777777}
778778
779779func validateAgentConfigVersionCompatibility (config * Config ) error {
780- if config .CertificateManagement == "" {
780+ return validateAgentConfigVersionCompatibilityWithMode (config , false )
781+ }
782+
783+ func validateAgentConfigVersionCompatibilityWithMode (config * Config , isCertManagerMode bool ) error {
784+ if config .Version == "" {
781785 if len (config .Certificates ) > 0 {
782- return fmt .Errorf ("certificates are configured but 'certificate-management' version is not specified." )
786+ return fmt .Errorf ("certificates are configured but 'version' field is not specified. Add 'version: v1' to your config " )
783787 }
784788 return nil
785789 }
786790
787- switch config .CertificateManagement {
791+ switch config .Version {
788792 case "v1" :
789- return validateCertificateManagementV1 (config )
793+ if isCertManagerMode {
794+ return validateCertificateManagementV1ForCertManager (config )
795+ } else {
796+ return validateCertificateManagementV1 (config )
797+ }
790798 default :
791- return fmt .Errorf ("unsupported certificate-management version: %s. Supported versions: v1" , config .CertificateManagement )
799+ return fmt .Errorf ("unsupported version: %s. Supported versions: v1" , config .Version )
792800 }
793801}
794802
795803func validateCertificateManagementV1 (config * Config ) error {
796- if len (config .Templates ) > 0 {
797- return fmt .Errorf ("certificate-management: v1 does not support 'templates' configuration. Templates are for secret management, not certificate management. Please remove the templates section or use a different configuration mode" )
798- }
804+ return fmt .Errorf ("version: v1 is for certificate management. Please use 'infisical cert-manager agent' for certificate configurations" )
805+ }
799806
807+ func validateCertificateManagementV1ForCertManager (config * Config ) error {
800808 if len (config .Certificates ) == 0 {
801- return fmt .Errorf ("certificate- management: v1 requires at least one certificate to be configured in the 'certificates' section " )
809+ return fmt .Errorf ("certificate management requires at least one certificate to be configured" )
802810 }
803-
804- log .Info ().Msg ("Configuration validated for certificate-management: v1" )
805811 return nil
806812}
807813
808814func ParseAgentConfig (configFile []byte ) (* Config , error ) {
815+ return parseAgentConfigWithMode (configFile , false )
816+ }
817+
818+ func ParseAgentConfigForCertManager (configFile []byte ) (* Config , error ) {
819+ return parseAgentConfigWithMode (configFile , true )
820+ }
821+
822+ func parseAgentConfigWithMode (configFile []byte , isCertManagerMode bool ) (* Config , error ) {
809823 var rawConfig Config
810824
811825 if err := yaml .Unmarshal (configFile , & rawConfig ); err != nil {
@@ -827,7 +841,7 @@ func ParseAgentConfig(configFile []byte) (*Config, error) {
827841
828842 log .Info ().Msgf ("Infisical instance address set to %s" , rawConfig .Infisical .Address )
829843
830- if err := validateAgentConfigVersionCompatibility (& rawConfig ); err != nil {
844+ if err := validateAgentConfigVersionCompatibilityWithMode (& rawConfig , isCertManagerMode ); err != nil {
831845 return nil , err
832846 }
833847
@@ -3022,7 +3036,7 @@ var agentCmd = &cobra.Command{
30223036 }
30233037
30243038 var certificates []AgentCertificateConfig
3025- if agentConfig .CertificateManagement != "" {
3039+ if agentConfig .Version != "" {
30263040 certificates = agentConfig .Certificates
30273041 }
30283042
@@ -3176,11 +3190,228 @@ var agentCmd = &cobra.Command{
31763190 },
31773191}
31783192
3193+ func validateCertificateOnlyMode (config * Config ) error {
3194+ if config .Version != "v1" {
3195+ return fmt .Errorf ("certificate management requires version: v1" )
3196+ }
3197+
3198+ if len (config .Certificates ) == 0 {
3199+ return fmt .Errorf ("certificate management requires at least one certificate to be configured" )
3200+ }
3201+
3202+ if len (config .Templates ) > 0 {
3203+ return fmt .Errorf ("certificate-only mode does not support templates. Use regular 'infisical agent' for secrets management" )
3204+ }
3205+
3206+ return nil
3207+ }
3208+
3209+ var certManagerCmd = & cobra.Command {
3210+ Use : "cert-manager" ,
3211+ Short : "Certificate management commands" ,
3212+ Long : "Commands for managing certificates through the Infisical agent" ,
3213+ }
3214+
3215+ var certManagerAgentCmd = & cobra.Command {
3216+ Example : `
3217+ infisical cert-manager agent --config certificate-agent-config.yaml
3218+ ` ,
3219+ Use : "agent" ,
3220+ Short : "Launch certificate management agent" ,
3221+ Long : "Used to launch a client daemon specifically for certificate management and lifecycle automation" ,
3222+ DisableFlagsInUseLine : true ,
3223+ Run : func (cmd * cobra.Command , args []string ) {
3224+
3225+ log .Info ().Msg ("starting Infisical certificate management agent..." )
3226+
3227+ configPath , err := cmd .Flags ().GetString ("config" )
3228+ if err != nil {
3229+ util .HandleError (err , "Unable to parse flag config" )
3230+ }
3231+
3232+ var agentConfigInBytes []byte
3233+
3234+ agentConfigInBase64 := os .Getenv ("INFISICAL_AGENT_CONFIG_BASE64" )
3235+
3236+ if agentConfigInBase64 == "" {
3237+ data , err := ioutil .ReadFile (configPath )
3238+ if err != nil {
3239+ if ! FileExists (configPath ) {
3240+ log .Error ().Msgf ("Unable to locate %s. The provided agent config file path is either missing or incorrect" , configPath )
3241+ return
3242+ }
3243+ }
3244+ agentConfigInBytes = data
3245+ }
3246+
3247+ if agentConfigInBase64 != "" {
3248+ decodedAgentConfig , err := base64 .StdEncoding .DecodeString (agentConfigInBase64 )
3249+ if err != nil {
3250+ log .Error ().Msgf ("Unable to decode base64 config file because %v" , err )
3251+ return
3252+ }
3253+
3254+ agentConfigInBytes = decodedAgentConfig
3255+ }
3256+
3257+ if ! FileExists (configPath ) && agentConfigInBase64 == "" {
3258+ log .Error ().Msgf ("No agent config file provided at %v. Please provide a agent config file" , configPath )
3259+ return
3260+ }
3261+
3262+ agentConfig , err := ParseAgentConfigForCertManager (agentConfigInBytes )
3263+ if err != nil {
3264+ log .Error ().Msgf ("Unable to parse %s because %v. Please ensure that it follows the Infisical Agent config structure" , configPath , err )
3265+ return
3266+ }
3267+
3268+ if err := validateCertificateOnlyMode (agentConfig ); err != nil {
3269+ log .Error ().Msgf ("Certificate-only mode validation failed: %v" , err )
3270+ return
3271+ }
3272+
3273+ err = processCertificateCSRPaths (& agentConfig .Certificates )
3274+ if err != nil {
3275+ log .Error ().Msgf ("Failed to load CSR files: %v" , err )
3276+ return
3277+ }
3278+
3279+ err = validateCertificateLifecycleConfig (& agentConfig .Certificates )
3280+ if err != nil {
3281+ log .Error ().Msgf ("Certificate lifecycle configuration validation failed: %v" , err )
3282+ return
3283+ }
3284+
3285+ authMethodValid , authStrategy := util .IsAuthMethodValid (agentConfig .Auth .Type , false )
3286+
3287+ if ! authMethodValid {
3288+ util .PrintErrorMessageAndExit (fmt .Sprintf ("The auth method '%s' is not supported." , agentConfig .Auth .Type ))
3289+ }
3290+
3291+ ctx , cancel := context .WithCancel (context .Background ())
3292+
3293+ tokenRefreshNotifier := make (chan bool )
3294+ sigChan := make (chan os.Signal , 1 )
3295+ signal .Notify (sigChan , syscall .SIGINT , syscall .SIGTERM , syscall .SIGQUIT )
3296+
3297+ filePaths := agentConfig .Sinks
3298+
3299+ configBytes , err := yaml .Marshal (agentConfig .Auth .Config )
3300+ if err != nil {
3301+ log .Error ().Msgf ("unable to marshal auth config because %v" , err )
3302+ cancel ()
3303+ return
3304+ }
3305+
3306+ tm := NewAgentManager (NewAgentMangerOptions {
3307+ FileDeposits : filePaths ,
3308+ Templates : []Template {}, // No templates in cert-only mode
3309+ Certificates : agentConfig .Certificates ,
3310+ AuthConfigBytes : configBytes ,
3311+ NewAccessTokenNotificationChan : tokenRefreshNotifier ,
3312+ ExitAfterAuth : agentConfig .Infisical .ExitAfterAuth ,
3313+ AuthStrategy : authStrategy ,
3314+ RevokeCredentialsOnShutdown : agentConfig .Infisical .RevokeCredentialsOnShutdown ,
3315+ RetryConfig : agentConfig .Infisical .RetryConfig ,
3316+ })
3317+
3318+ tm .cacheManager , err = NewCacheManager (ctx , & agentConfig .Cache )
3319+ if err != nil {
3320+ log .Error ().Msgf ("unable to setup cache manager: %v" , err )
3321+ cancel ()
3322+ return
3323+ }
3324+ tm .dynamicSecretLeases = NewDynamicSecretLeaseManager (tm .cacheManager , tm .SdkRetryConfig ())
3325+
3326+ go tm .ManageTokenLifecycle ()
3327+
3328+ if len (agentConfig .Certificates ) > 0 {
3329+ go func () {
3330+ for {
3331+ if tm .getTokenUnsafe () != "" {
3332+ break
3333+ }
3334+ time .Sleep (100 * time .Millisecond )
3335+ }
3336+
3337+ httpClient , err := tm .createAuthenticatedClient ()
3338+ if err != nil {
3339+ log .Error ().Msgf ("failed to create authenticated client for name resolution: %v" , err )
3340+ return
3341+ }
3342+
3343+ err = resolveCertificateNameReferences (& agentConfig .Certificates , httpClient )
3344+ if err != nil {
3345+ log .Error ().Msgf ("failed to resolve certificate name references: %v" , err )
3346+ return
3347+ }
3348+
3349+ for i := range tm .certificates {
3350+ for j := range agentConfig .Certificates {
3351+ if tm .certificates [i ].ID == j + 1 {
3352+ tm .certificates [i ].Certificate = agentConfig .Certificates [j ]
3353+ break
3354+ }
3355+ }
3356+ }
3357+ }()
3358+ }
3359+
3360+ if len (tm .certificates ) > 0 {
3361+ log .Info ().Msg ("certificate management engine starting..." )
3362+ go tm .MonitorCertificates (ctx )
3363+ }
3364+
3365+ for {
3366+ select {
3367+ case <- tokenRefreshNotifier :
3368+ go tm .WriteTokenToFiles ()
3369+ case <- sigChan :
3370+ tm .isShuttingDown = true
3371+ tm .cancelContext ()
3372+ log .Info ().Msg ("certificate management agent is gracefully shutting down..." )
3373+ cancel ()
3374+
3375+ exitCode := 0
3376+
3377+ if ! tm .exitAfterAuth && tm .revokeCredentialsOnShutdown {
3378+
3379+ done := make (chan error , 1 )
3380+
3381+ go func () {
3382+ done <- tm .RevokeCredentials ()
3383+ }()
3384+
3385+ select {
3386+ case err := <- done :
3387+ if err != nil {
3388+ log .Error ().Msgf ("unable to revoke credentials [err=%v]" , err )
3389+ exitCode = 1
3390+ }
3391+ case <- time .After (5 * time .Minute ):
3392+ log .Warn ().Msg ("credential revocation timed out after 5 minutes, forcing exit" )
3393+ exitCode = 1
3394+ }
3395+
3396+ }
3397+
3398+ os .Exit (exitCode )
3399+ }
3400+ }
3401+
3402+ },
3403+ }
3404+
31793405func init () {
31803406 agentCmd .SetHelpFunc (func (command * cobra.Command , strings []string ) {
31813407 command .Flags ().MarkHidden ("domain" )
31823408 command .Parent ().HelpFunc ()(command , strings )
31833409 })
31843410 agentCmd .Flags ().String ("config" , "agent-config.yaml" , "The path to agent config yaml file" )
3411+
3412+ certManagerAgentCmd .Flags ().String ("config" , "certificate-agent-config.yaml" , "The path to certificate agent config yaml file" )
3413+ certManagerCmd .AddCommand (certManagerAgentCmd )
3414+
31853415 rootCmd .AddCommand (agentCmd )
3416+ rootCmd .AddCommand (certManagerCmd )
31863417}
0 commit comments