diff --git a/internal/templates/userdata/windows_wrapper.tmpl b/internal/templates/userdata/windows_wrapper.tmpl index f2b040ed..ec58a87c 100644 --- a/internal/templates/userdata/windows_wrapper.tmpl +++ b/internal/templates/userdata/windows_wrapper.tmpl @@ -1,4 +1,5 @@ $ErrorActionPreference="Stop" +Set-ExecutionPolicy RemoteSigned function Start-ExecuteWithRetry { [CmdletBinding()] @@ -49,9 +50,9 @@ function Start-ExecuteWithRetry { } } -$installScript = (Join-Path $env:TMP, "garm-install.ps1") +$installScript = (Join-Path $env:TMP "garm-install.ps1") Start-ExecuteWithRetry -ScriptBlock { wget -UseBasicParsing -Headers @{"Accept"="application/json"; "Authorization"="Bearer {{ .CallbackToken }}"} -Uri {{ .MetadataURL }}/install-script/ -OutFile $installScript } -MaxRetryCount 5 -RetryInterval 5 -RetryMessage "Retrying download of runner install script..." -$installScript \ No newline at end of file +powershell.exe -Sta -NonInteractive -ExecutionPolicy RemoteSigned -File $installScript \ No newline at end of file diff --git a/runner/metadata.go b/runner/metadata.go index 17cf0caa..a4567c52 100644 --- a/runner/metadata.go +++ b/runner/metadata.go @@ -351,7 +351,7 @@ func (r *Runner) GetInstanceGithubRegistrationToken(ctx context.Context) (string poolMgr, err := r.getPoolManagerFromInstance(ctx, instance) if err != nil { - return "", fmt.Errorf("error fetching pool manager for instance: %w", err) + return "", fmt.Errorf("error fetching pool manager for instance %s (%s): %w", instance.Name, instance.PoolID, err) } token, err := poolMgr.GithubRunnerRegistrationToken() @@ -383,17 +383,17 @@ func (r *Runner) GetRootCertificateBundle(ctx context.Context) (params.Certifica return params.CertificateBundle{}, runnerErrors.ErrUnauthorized } - poolMgr, err := r.getPoolManagerFromInstance(ctx, instance) + entity, err := auth.InstanceEntity(ctx) if err != nil { - return params.CertificateBundle{}, fmt.Errorf("error fetching pool manager for instance: %w", err) + slog.ErrorContext(r.ctx, "failed to get entity", "error", err) + return params.CertificateBundle{}, runnerErrors.ErrUnauthorized } - bundle, err := poolMgr.RootCABundle() + bundle, err := entity.Credentials.RootCertificateBundle() if err != nil { slog.With(slog.Any("error", err)).ErrorContext( ctx, "failed to get root CA bundle", - "instance", instance.Name, - "pool_manager", poolMgr.ID()) + "instance", instance.Name) // The root CA bundle is invalid. Return an empty bundle to the runner and log the event. return params.CertificateBundle{ RootCertificates: make(map[string][]byte), diff --git a/runner/metadata_test.go b/runner/metadata_test.go index be2b8832..7a87bdf2 100644 --- a/runner/metadata_test.go +++ b/runner/metadata_test.go @@ -18,6 +18,8 @@ import ( "context" "encoding/base64" "fmt" + "os" + "path/filepath" "testing" "github.com/stretchr/testify/mock" @@ -450,26 +452,6 @@ func (s *MetadataTestSuite) TestGetInstanceGithubRegistrationTokenJITConfig() { s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized) } -func (s *MetadataTestSuite) TestGetRootCertificateBundle() { - expectedBundle := params.CertificateBundle{ - RootCertificates: map[string][]byte{ - "test-ca": []byte("test-certificate"), - }, - } - - // Set up mocks - s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) - s.Fixtures.PoolMgrMock.On("RootCABundle").Return(expectedBundle, nil) - - bundle, err := s.Runner.GetRootCertificateBundle(s.instanceCtx) - - s.Require().Nil(err) - s.Require().Equal(expectedBundle.RootCertificates, bundle.RootCertificates) - - s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) - s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) -} - func (s *MetadataTestSuite) TestGetRootCertificateBundleUnauthorized() { _, err := s.Runner.GetRootCertificateBundle(s.unauthorizedCtx) @@ -477,20 +459,39 @@ func (s *MetadataTestSuite) TestGetRootCertificateBundleUnauthorized() { s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized) } -func (s *MetadataTestSuite) TestGetRootCertificateBundleInvalidBundle() { - // Set up mocks to return error for invalid bundle - s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) - s.Fixtures.PoolMgrMock.On("RootCABundle").Return(params.CertificateBundle{}, fmt.Errorf("invalid bundle")) - s.Fixtures.PoolMgrMock.On("ID").Return("test-pool-manager-id") +func (s *MetadataTestSuite) TestGetRootCertificateBundleAuthorized() { + // Load a valid test certificate from testdata + certPath := filepath.Join("../testdata/certs", "srv-pub.pem") + testCertPEM, err := os.ReadFile(certPath) + s.Require().NoError(err, "Failed to read test certificate") - bundle, err := s.Runner.GetRootCertificateBundle(s.instanceCtx) + // Set up entity with valid CA bundle + entity := s.Fixtures.TestEntity + entity.Credentials.CABundle = testCertPEM + ctx := auth.SetInstanceParams(context.Background(), s.Fixtures.TestInstance) + ctx = auth.SetInstanceEntity(ctx, entity) + + bundle, err := s.Runner.GetRootCertificateBundle(ctx) + + s.Require().Nil(err) + s.Require().NotNil(bundle.RootCertificates) + s.Require().NotEmpty(bundle.RootCertificates) + // The test certificate file contains 2 certificates + s.Require().Len(bundle.RootCertificates, 2) +} + +func (s *MetadataTestSuite) TestGetRootCertificateBundleInvalidBundle() { + // Set up entity with invalid CA bundle (invalid PEM data) + entity := s.Fixtures.TestEntity + entity.Credentials.CABundle = []byte("bogus cert") + ctx := auth.SetInstanceParams(context.Background(), s.Fixtures.TestInstance) + ctx = auth.SetInstanceEntity(ctx, entity) + + bundle, err := s.Runner.GetRootCertificateBundle(ctx) // Should return empty bundle without error when CA bundle is invalid s.Require().Nil(err) s.Require().Empty(bundle.RootCertificates) - - s.Fixtures.PoolMgrMock.AssertExpectations(s.T()) - s.Fixtures.PoolMgrCtrlMock.AssertExpectations(s.T()) } func TestMetadataTestSuite(t *testing.T) {