mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 14:21:37 +00:00
197 lines
7.4 KiB
Go
197 lines
7.4 KiB
Go
package iis
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/shankar0123/certctl/internal/connector/target"
|
|
)
|
|
|
|
// Config represents the IIS deployment target configuration.
|
|
// This configuration is for Windows agents that manage IIS servers.
|
|
type Config struct {
|
|
Hostname string `json:"hostname"` // Target hostname or IP
|
|
SiteName string `json:"site_name"` // IIS site name (e.g., "Default Web Site")
|
|
CertStore string `json:"cert_store"` // Windows cert store (e.g., "My", "WebHosting")
|
|
BindingInfo string `json:"binding_info"` // Binding info (e.g., "*.example.com")
|
|
}
|
|
|
|
// Connector implements the target.Connector interface for IIS (Internet Information Services).
|
|
// This connector runs on Windows agents and manages certificate deployment via IIS.
|
|
//
|
|
// IIS certificate management requires:
|
|
// - Windows Server with IIS installed
|
|
// - PowerShell execution available
|
|
// - Administrative privileges
|
|
//
|
|
// TODO: Implement actual PowerShell command execution for:
|
|
// - Certificate import: Import-PfxCertificate
|
|
// - IIS binding update: New-WebBinding, Set-WebBinding
|
|
// - Validation: Get-WebBinding
|
|
type Connector struct {
|
|
config *Config
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// New creates a new IIS target connector with the given configuration and logger.
|
|
func New(config *Config, logger *slog.Logger) *Connector {
|
|
return &Connector{
|
|
config: config,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// ValidateConfig checks that the IIS configuration is valid and accessible.
|
|
// It verifies that we're on Windows and that the IIS site exists.
|
|
//
|
|
// TODO: Implement actual PowerShell checks.
|
|
func (c *Connector) ValidateConfig(ctx context.Context, rawConfig json.RawMessage) error {
|
|
var cfg Config
|
|
if err := json.Unmarshal(rawConfig, &cfg); err != nil {
|
|
return fmt.Errorf("invalid IIS config: %w", err)
|
|
}
|
|
|
|
if cfg.SiteName == "" || cfg.CertStore == "" {
|
|
return fmt.Errorf("IIS site_name and cert_store are required")
|
|
}
|
|
|
|
// Verify we're on Windows
|
|
if runtime.GOOS != "windows" {
|
|
return fmt.Errorf("IIS connector only runs on Windows, got %s", runtime.GOOS)
|
|
}
|
|
|
|
c.logger.Info("validating IIS configuration",
|
|
"site_name", cfg.SiteName,
|
|
"cert_store", cfg.CertStore,
|
|
"hostname", cfg.Hostname)
|
|
|
|
// TODO: Implement PowerShell check
|
|
// In production:
|
|
// 1. Run PowerShell command: Get-IISSite -Name {SiteName}
|
|
// 2. Verify site exists and is running
|
|
// 3. Check cert store: Get-Item -Path "Cert:\LocalMachine\{CertStore}"
|
|
|
|
c.logger.Warn("IIS validation not yet fully implemented",
|
|
"site_name", cfg.SiteName)
|
|
|
|
c.config = &cfg
|
|
return nil
|
|
}
|
|
|
|
// DeployCertificate imports a certificate to the Windows certificate store and updates
|
|
// the IIS binding to use the new certificate.
|
|
//
|
|
// The IIS deployment process (via PowerShell):
|
|
// 1. Create a temporary PFX file from the certificate and existing private key
|
|
// (Note: The private key is managed by the agent, not provided by the control plane)
|
|
// 2. Import the PFX to the Windows certificate store (My store by default)
|
|
// 3. Get the certificate thumbprint
|
|
// 4. Update the IIS binding to use the new certificate by thumbprint
|
|
// 5. Verify the binding is active
|
|
//
|
|
// TODO: Implement actual PowerShell commands:
|
|
// - Import-PfxCertificate -FilePath {pfxPath} -CertStoreLocation "Cert:\LocalMachine\My"
|
|
// - Get-ChildItem -Path "Cert:\LocalMachine\My" | Where {$_.Subject -eq "CN=..."}
|
|
// - Set-WebBinding -Name {SiteName} -BindingInformation "{BindingInfo}" -Protocol https -SslFlags 1 -CertificateThumbprint {thumbprint}
|
|
func (c *Connector) DeployCertificate(ctx context.Context, request target.DeploymentRequest) (*target.DeploymentResult, error) {
|
|
c.logger.Info("deploying certificate to IIS",
|
|
"site_name", c.config.SiteName,
|
|
"cert_store", c.config.CertStore)
|
|
|
|
startTime := time.Now()
|
|
|
|
// TODO: Implement IIS certificate deployment
|
|
// In production:
|
|
// 1. Create temporary PFX from CertPEM and ChainPEM
|
|
// (Private key should already exist on the agent)
|
|
// 2. Import certificate:
|
|
// PowerShell: Import-PfxCertificate -FilePath $pfxPath -CertStoreLocation "Cert:\LocalMachine\{CertStore}" -Password $password
|
|
// 3. Get certificate thumbprint:
|
|
// PowerShell: (Get-ChildItem -Path "Cert:\LocalMachine\{CertStore}" | Where {$_.Subject -like "*CN=*"}).Thumbprint
|
|
// 4. Update IIS binding:
|
|
// PowerShell: Set-WebBinding -Name "{SiteName}" -BindingInformation "{BindingInfo}:443:*.example.com" -Protocol https -CertificateThumbprint $thumbprint
|
|
// 5. Remove temporary PFX file
|
|
|
|
deploymentDuration := time.Since(startTime)
|
|
|
|
c.logger.Warn("IIS deployment not yet implemented",
|
|
"site_name", c.config.SiteName)
|
|
|
|
return &target.DeploymentResult{
|
|
Success: true,
|
|
TargetAddress: fmt.Sprintf("%s (IIS: %s)", c.config.Hostname, c.config.SiteName),
|
|
DeploymentID: fmt.Sprintf("iis-%d", time.Now().Unix()),
|
|
Message: "Certificate deployment to IIS initiated (stub)",
|
|
DeployedAt: time.Now(),
|
|
Metadata: map[string]string{
|
|
"hostname": c.config.Hostname,
|
|
"site_name": c.config.SiteName,
|
|
"cert_store": c.config.CertStore,
|
|
"duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// ValidateDeployment verifies that the certificate is properly deployed in IIS.
|
|
// It checks the IIS binding configuration to ensure it's active with the correct certificate.
|
|
//
|
|
// TODO: Implement actual PowerShell validation.
|
|
// PowerShell command:
|
|
// - Get-IISSiteBinding -Name {SiteName} | Where {$_.protocol -eq "https"}
|
|
func (c *Connector) ValidateDeployment(ctx context.Context, request target.ValidationRequest) (*target.ValidationResult, error) {
|
|
c.logger.Info("validating IIS deployment",
|
|
"certificate_id", request.CertificateID,
|
|
"serial", request.Serial,
|
|
"site_name", c.config.SiteName)
|
|
|
|
startTime := time.Now()
|
|
|
|
// TODO: Implement IIS deployment validation
|
|
// In production:
|
|
// 1. Query IIS binding status:
|
|
// PowerShell: Get-WebBinding -Name "{SiteName}" -Protocol "https"
|
|
// 2. Verify binding exists and is active
|
|
// 3. Extract certificate thumbprint from binding
|
|
// 4. Query certificate store to verify thumbprint matches expected certificate
|
|
// 5. Check certificate validity dates and key match
|
|
|
|
validationDuration := time.Since(startTime)
|
|
|
|
c.logger.Warn("IIS validation not yet implemented",
|
|
"site_name", c.config.SiteName)
|
|
|
|
return &target.ValidationResult{
|
|
Valid: true,
|
|
Serial: request.Serial,
|
|
TargetAddress: fmt.Sprintf("%s (IIS: %s)", c.config.Hostname, c.config.SiteName),
|
|
Message: "Certificate deployment validation initiated (stub)",
|
|
ValidatedAt: time.Now(),
|
|
Metadata: map[string]string{
|
|
"hostname": c.config.Hostname,
|
|
"site_name": c.config.SiteName,
|
|
"duration_ms": fmt.Sprintf("%d", validationDuration.Milliseconds()),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// executePowerShellCommand is a helper to run PowerShell commands on Windows.
|
|
// It's a stub implementation that documents the pattern for actual PS execution.
|
|
func (c *Connector) executePowerShellCommand(ctx context.Context, psCommand string) (string, error) {
|
|
if runtime.GOOS != "windows" {
|
|
return "", fmt.Errorf("PowerShell commands only work on Windows")
|
|
}
|
|
|
|
// TODO: Implement actual PowerShell execution
|
|
// In production:
|
|
// cmd := exec.CommandContext(ctx, "powershell", "-NoProfile", "-Command", psCommand)
|
|
// output, err := cmd.CombinedOutput()
|
|
// return string(output), err
|
|
|
|
c.logger.Debug("executing PowerShell command", "command", psCommand)
|
|
return "", nil
|
|
}
|